payload 3.89.0-internal.4b0e9b6 → 3.89.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 (51) hide show
  1. package/dist/collections/operations/update.js +5 -5
  2. package/dist/collections/operations/update.js.map +1 -1
  3. package/dist/index.bundled.d.ts +49 -29
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/queues/config/collection.d.ts.map +1 -1
  9. package/dist/queues/config/collection.js +6 -0
  10. package/dist/queues/config/collection.js.map +1 -1
  11. package/dist/queues/config/types/index.d.ts +2 -0
  12. package/dist/queues/config/types/index.d.ts.map +1 -1
  13. package/dist/queues/config/types/index.js.map +1 -1
  14. package/dist/queues/endpoints/handleSchedules.d.ts.map +1 -1
  15. package/dist/queues/endpoints/handleSchedules.js +2 -1
  16. package/dist/queues/endpoints/handleSchedules.js.map +1 -1
  17. package/dist/queues/endpoints/run.d.ts.map +1 -1
  18. package/dist/queues/endpoints/run.js +2 -1
  19. package/dist/queues/endpoints/run.js.map +1 -1
  20. package/dist/uploads/getFileContentRequirement.d.ts +9 -0
  21. package/dist/uploads/getFileContentRequirement.d.ts.map +1 -0
  22. package/dist/uploads/getFileContentRequirement.js +24 -0
  23. package/dist/uploads/getFileContentRequirement.js.map +1 -0
  24. package/dist/uploads/getFileContentRequirement.spec.js +95 -0
  25. package/dist/uploads/getFileContentRequirement.spec.js.map +1 -0
  26. package/dist/uploads/getFileFromClientUpload.d.ts +20 -0
  27. package/dist/uploads/getFileFromClientUpload.d.ts.map +1 -0
  28. package/dist/uploads/getFileFromClientUpload.js +199 -0
  29. package/dist/uploads/getFileFromClientUpload.js.map +1 -0
  30. package/dist/uploads/getFileFromClientUpload.spec.js +590 -0
  31. package/dist/uploads/getFileFromClientUpload.spec.js.map +1 -0
  32. package/dist/uploads/unlinkClientUploadTempFile.d.ts +20 -0
  33. package/dist/uploads/unlinkClientUploadTempFile.d.ts.map +1 -0
  34. package/dist/uploads/unlinkClientUploadTempFile.js +30 -0
  35. package/dist/uploads/unlinkClientUploadTempFile.js.map +1 -0
  36. package/dist/uploads/unlinkTempFiles.d.ts.map +1 -1
  37. package/dist/uploads/unlinkTempFiles.js +10 -2
  38. package/dist/uploads/unlinkTempFiles.js.map +1 -1
  39. package/dist/uploads/unlinkTempFiles.spec.js +217 -0
  40. package/dist/uploads/unlinkTempFiles.spec.js.map +1 -0
  41. package/dist/utilities/addDataAndFileToRequest.d.ts.map +1 -1
  42. package/dist/utilities/addDataAndFileToRequest.js +7 -46
  43. package/dist/utilities/addDataAndFileToRequest.js.map +1 -1
  44. package/dist/utilities/addDataAndFileToRequest.spec.js +131 -1
  45. package/dist/utilities/addDataAndFileToRequest.spec.js.map +1 -1
  46. package/dist/utilities/wrapInternalEndpoints.d.ts.map +1 -1
  47. package/dist/utilities/wrapInternalEndpoints.js +10 -3
  48. package/dist/utilities/wrapInternalEndpoints.js.map +1 -1
  49. package/dist/utilities/wrapInternalEndpoints.spec.js +124 -0
  50. package/dist/utilities/wrapInternalEndpoints.spec.js.map +1 -0
  51. package/package.json +2 -2
@@ -0,0 +1,590 @@
1
+ import fs from 'fs/promises';
2
+ import os from 'os';
3
+ import { afterEach, describe, expect, it, vi } from 'vitest';
4
+ import { HEADER_PROBE_BYTE_LENGTH } from './getFileContentRequirement.js';
5
+ import { getFileFromClientUpload } from './getFileFromClientUpload.js';
6
+ import { getImageSize } from './getImageSize.js';
7
+ const MINIMAL_PNG = (()=>{
8
+ const signature = Buffer.from([
9
+ 0x89,
10
+ 0x50,
11
+ 0x4e,
12
+ 0x47,
13
+ 0x0d,
14
+ 0x0a,
15
+ 0x1a,
16
+ 0x0a
17
+ ]);
18
+ const ihdrData = Buffer.alloc(13);
19
+ ihdrData.writeUInt32BE(400, 0); // width
20
+ ihdrData.writeUInt32BE(300, 4); // height
21
+ ihdrData[8] = 8; // bit depth
22
+ ihdrData[9] = 2; // color type: RGB
23
+ const ihdrChunk = Buffer.concat([
24
+ Buffer.from([
25
+ 0,
26
+ 0,
27
+ 0,
28
+ 13
29
+ ]),
30
+ Buffer.from('IHDR'),
31
+ ihdrData,
32
+ Buffer.alloc(4)
33
+ ]);
34
+ return Buffer.concat([
35
+ signature,
36
+ ihdrChunk
37
+ ]);
38
+ })();
39
+ const tempFilesToRemove = [];
40
+ afterEach(async ()=>{
41
+ vi.unstubAllGlobals();
42
+ for (const tempFilePath of tempFilesToRemove){
43
+ await fs.rm(tempFilePath, {
44
+ force: true
45
+ });
46
+ }
47
+ tempFilesToRemove.length = 0;
48
+ });
49
+ const createReq = ({ handlers, upload })=>{
50
+ const request = new Request('http://localhost/api/media');
51
+ const req = request;
52
+ req.query = {};
53
+ req.payload = {
54
+ collections: {
55
+ media: {
56
+ config: {
57
+ upload: {
58
+ disableLocalStorage: true,
59
+ handlers,
60
+ ...upload
61
+ }
62
+ }
63
+ }
64
+ },
65
+ config: {
66
+ upload: {}
67
+ },
68
+ logger: {
69
+ error: vi.fn()
70
+ }
71
+ };
72
+ return req;
73
+ };
74
+ const videoFile = (overrides = {})=>({
75
+ clientUploadContext: {
76
+ prefix: 'abc'
77
+ },
78
+ collectionSlug: 'media',
79
+ filename: 'clip.mp4',
80
+ mimeType: 'video/mp4',
81
+ size: 10,
82
+ ...overrides
83
+ });
84
+ const imageFile = (overrides = {})=>({
85
+ clientUploadContext: {
86
+ prefix: 'abc'
87
+ },
88
+ collectionSlug: 'media',
89
+ filename: 'photo.png',
90
+ mimeType: 'image/png',
91
+ size: MINIMAL_PNG.length,
92
+ ...overrides
93
+ });
94
+ describe('getFileFromClientUpload', ()=>{
95
+ it('does not call a handler and returns empty data with an own clientUploadContext property when no content is required', async ()=>{
96
+ const handler = vi.fn();
97
+ const req = createReq({
98
+ handlers: [
99
+ handler
100
+ ],
101
+ upload: {
102
+ disableLocalStorage: true
103
+ }
104
+ });
105
+ const file = videoFile();
106
+ const result = await getFileFromClientUpload({
107
+ file,
108
+ req
109
+ });
110
+ expect(handler).not.toHaveBeenCalled();
111
+ expect(Object.prototype.hasOwnProperty.call(result, 'clientUploadContext')).toBe(true);
112
+ expect(result.clientUploadContext).toEqual({
113
+ prefix: 'abc'
114
+ });
115
+ expect(result.data.length).toBe(0);
116
+ expect(result.tempFilePath).toBeUndefined();
117
+ expect(result.name).toBe('clip.mp4');
118
+ expect(result.size).toBe(10);
119
+ });
120
+ it('requests a bounded byte range and returns dimensions for a header-only image', async ()=>{
121
+ const handler = vi.fn(async (handlerReq)=>{
122
+ expect(handlerReq.headers.get('Range')).toBe(`bytes=0-${HEADER_PROBE_BYTE_LENGTH - 1}`);
123
+ return new Response(MINIMAL_PNG, {
124
+ headers: {
125
+ 'Content-Type': 'image/png'
126
+ },
127
+ status: 206
128
+ });
129
+ });
130
+ const req = createReq({
131
+ handlers: [
132
+ handler
133
+ ],
134
+ upload: {
135
+ disableLocalStorage: true
136
+ }
137
+ });
138
+ const file = imageFile();
139
+ const result = await getFileFromClientUpload({
140
+ file,
141
+ req
142
+ });
143
+ expect(result.data.length).toBeLessThanOrEqual(HEADER_PROBE_BYTE_LENGTH);
144
+ expect(result.tempFilePath).toBeUndefined();
145
+ await expect(getImageSize({
146
+ file: result
147
+ })).resolves.toEqual({
148
+ height: 300,
149
+ width: 400
150
+ });
151
+ });
152
+ it('caps the header read at the boundary and cancels the stream without pulling past it', async ()=>{
153
+ let hasCancelled = false;
154
+ let hasReadPastBoundary = false;
155
+ const firstChunk = Buffer.concat([
156
+ MINIMAL_PNG,
157
+ Buffer.alloc(HEADER_PROBE_BYTE_LENGTH - MINIMAL_PNG.length)
158
+ ]);
159
+ const stream = new ReadableStream({
160
+ cancel () {
161
+ hasCancelled = true;
162
+ },
163
+ pull (controller) {
164
+ hasReadPastBoundary = true;
165
+ controller.error(new Error('Read past header boundary'));
166
+ },
167
+ start (controller) {
168
+ controller.enqueue(firstChunk);
169
+ }
170
+ }, {
171
+ highWaterMark: 0
172
+ });
173
+ const handler = vi.fn(async ()=>new Response(stream, {
174
+ headers: {
175
+ 'Content-Type': 'image/png'
176
+ },
177
+ status: 206
178
+ }));
179
+ const req = createReq({
180
+ handlers: [
181
+ handler
182
+ ],
183
+ upload: {
184
+ disableLocalStorage: true
185
+ }
186
+ });
187
+ const file = imageFile({
188
+ size: HEADER_PROBE_BYTE_LENGTH
189
+ });
190
+ const result = await getFileFromClientUpload({
191
+ file,
192
+ req
193
+ });
194
+ expect(result.data.length).toBe(HEADER_PROBE_BYTE_LENGTH);
195
+ expect(hasCancelled).toBe(true);
196
+ expect(hasReadPastBoundary).toBe(false);
197
+ });
198
+ it('lets a handler read native Request properties like signal through the Range-scoped proxy', async ()=>{
199
+ const handler = vi.fn(async (handlerReq)=>{
200
+ expect(()=>handlerReq.signal).not.toThrow();
201
+ expect(handlerReq.signal).toBeInstanceOf(AbortSignal);
202
+ return new Response(MINIMAL_PNG, {
203
+ headers: {
204
+ 'Content-Type': 'image/png'
205
+ },
206
+ status: 206
207
+ });
208
+ });
209
+ const req = createReq({
210
+ handlers: [
211
+ handler
212
+ ],
213
+ upload: {
214
+ disableLocalStorage: true
215
+ }
216
+ });
217
+ const file = imageFile();
218
+ await getFileFromClientUpload({
219
+ file,
220
+ req
221
+ });
222
+ expect(handler).toHaveBeenCalledTimes(1);
223
+ });
224
+ it('falls back to a full fetch when the bounded header cannot be probed for dimensions', async ()=>{
225
+ const handler = vi.fn().mockResolvedValueOnce(new Response(Buffer.from('not an image'), {
226
+ headers: {
227
+ 'Content-Type': 'image/png'
228
+ },
229
+ status: 206
230
+ })).mockResolvedValueOnce(new Response(MINIMAL_PNG, {
231
+ headers: {
232
+ 'Content-Type': 'image/png'
233
+ },
234
+ status: 200
235
+ }));
236
+ const req = createReq({
237
+ handlers: [
238
+ handler
239
+ ],
240
+ upload: {
241
+ disableLocalStorage: true
242
+ }
243
+ });
244
+ const file = imageFile();
245
+ const result = await getFileFromClientUpload({
246
+ file,
247
+ req
248
+ });
249
+ tempFilesToRemove.push(result.tempFilePath);
250
+ expect(handler).toHaveBeenCalledTimes(2);
251
+ expect(result.tempFilePath).toBeDefined();
252
+ expect(result.data.length).toBe(0);
253
+ });
254
+ it('streams a full-content response to a temp file without buffering the whole body', async ()=>{
255
+ const chunks = [
256
+ Buffer.from('chunk-one-'),
257
+ Buffer.from('chunk-two-'),
258
+ Buffer.from('chunk-three')
259
+ ];
260
+ const stream = new ReadableStream({
261
+ start (controller) {
262
+ for (const chunk of chunks){
263
+ controller.enqueue(chunk);
264
+ }
265
+ controller.close();
266
+ }
267
+ });
268
+ const response = new Response(stream, {
269
+ headers: {
270
+ 'Content-Type': 'video/mp4'
271
+ },
272
+ status: 200
273
+ });
274
+ const arrayBufferTripwire = vi.fn(async ()=>{
275
+ throw new Error('Unexpected whole-body buffering');
276
+ });
277
+ Object.defineProperty(response, 'arrayBuffer', {
278
+ value: arrayBufferTripwire
279
+ });
280
+ const handler = vi.fn(async ()=>response);
281
+ const req = createReq({
282
+ handlers: [
283
+ handler
284
+ ],
285
+ upload: {
286
+ disableLocalStorage: true,
287
+ mimeTypes: [
288
+ 'video/*'
289
+ ]
290
+ }
291
+ });
292
+ const file = videoFile({
293
+ size: 30
294
+ });
295
+ const result = await getFileFromClientUpload({
296
+ file,
297
+ req
298
+ });
299
+ tempFilesToRemove.push(result.tempFilePath);
300
+ expect(result.data).toEqual(Buffer.alloc(0));
301
+ expect(arrayBufferTripwire).not.toHaveBeenCalled();
302
+ const written = await fs.readFile(result.tempFilePath);
303
+ expect(written.toString()).toBe(chunks.map((chunk)=>chunk.toString()).join(''));
304
+ });
305
+ it('runs every handler and uses the last one that returns a response, matching v3', async ()=>{
306
+ const firstHandler = vi.fn(async ()=>new Response(Buffer.from('first-response')));
307
+ const secondHandler = vi.fn(async ()=>new Response(Buffer.from('second-response')));
308
+ const req = createReq({
309
+ handlers: [
310
+ firstHandler,
311
+ secondHandler
312
+ ],
313
+ upload: {
314
+ disableLocalStorage: true,
315
+ mimeTypes: [
316
+ 'video/*'
317
+ ]
318
+ }
319
+ });
320
+ const file = videoFile();
321
+ const result = await getFileFromClientUpload({
322
+ file,
323
+ req
324
+ });
325
+ tempFilesToRemove.push(result.tempFilePath);
326
+ expect(firstHandler).toHaveBeenCalledTimes(1);
327
+ expect(secondHandler).toHaveBeenCalledTimes(1);
328
+ const written = await fs.readFile(result.tempFilePath);
329
+ expect(written.toString()).toBe('second-response');
330
+ });
331
+ it('falls through to the next handler when the first returns nothing', async ()=>{
332
+ const firstHandler = vi.fn(async ()=>undefined);
333
+ const secondHandler = vi.fn(async ()=>new Response(Buffer.alloc(10), {
334
+ status: 200
335
+ }));
336
+ const req = createReq({
337
+ handlers: [
338
+ firstHandler,
339
+ secondHandler
340
+ ],
341
+ upload: {
342
+ disableLocalStorage: true,
343
+ mimeTypes: [
344
+ 'video/*'
345
+ ]
346
+ }
347
+ });
348
+ const file = videoFile();
349
+ const result = await getFileFromClientUpload({
350
+ file,
351
+ req
352
+ });
353
+ tempFilesToRemove.push(result.tempFilePath);
354
+ expect(firstHandler).toHaveBeenCalledTimes(1);
355
+ expect(secondHandler).toHaveBeenCalledTimes(1);
356
+ });
357
+ it('follows exactly one redirect from a handler response', async ()=>{
358
+ const redirectTarget = 'http://storage.example.com/file.mp4';
359
+ const handler = vi.fn(async ()=>new Response(null, {
360
+ headers: {
361
+ Location: redirectTarget
362
+ },
363
+ status: 302
364
+ }));
365
+ const fetchMock = vi.fn(async ()=>new Response(Buffer.alloc(10), {
366
+ headers: {
367
+ 'Content-Type': 'video/mp4'
368
+ },
369
+ status: 200
370
+ }));
371
+ vi.stubGlobal('fetch', fetchMock);
372
+ const req = createReq({
373
+ handlers: [
374
+ handler
375
+ ],
376
+ upload: {
377
+ disableLocalStorage: true,
378
+ mimeTypes: [
379
+ 'video/*'
380
+ ]
381
+ }
382
+ });
383
+ const file = videoFile();
384
+ const result = await getFileFromClientUpload({
385
+ file,
386
+ req
387
+ });
388
+ tempFilesToRemove.push(result.tempFilePath);
389
+ expect(fetchMock).toHaveBeenCalledTimes(1);
390
+ expect(fetchMock).toHaveBeenCalledWith(redirectTarget);
391
+ });
392
+ it('forwards the Range header through a redirect during the bounded header probe', async ()=>{
393
+ const redirectTarget = 'http://storage.example.com/photo.png';
394
+ const handler = vi.fn(async ()=>new Response(null, {
395
+ headers: {
396
+ Location: redirectTarget
397
+ },
398
+ status: 302
399
+ }));
400
+ const fetchMock = vi.fn(async ()=>new Response(MINIMAL_PNG, {
401
+ headers: {
402
+ 'Content-Type': 'image/png'
403
+ },
404
+ status: 206
405
+ }));
406
+ vi.stubGlobal('fetch', fetchMock);
407
+ const req = createReq({
408
+ handlers: [
409
+ handler
410
+ ],
411
+ upload: {
412
+ disableLocalStorage: true
413
+ }
414
+ });
415
+ const file = imageFile();
416
+ const result = await getFileFromClientUpload({
417
+ file,
418
+ req
419
+ });
420
+ expect(result.tempFilePath).toBeUndefined();
421
+ expect(fetchMock).toHaveBeenCalledTimes(1);
422
+ expect(fetchMock).toHaveBeenCalledWith(redirectTarget, {
423
+ headers: {
424
+ Range: `bytes=0-${HEADER_PROBE_BYTE_LENGTH - 1}`
425
+ }
426
+ });
427
+ });
428
+ it('removes the partial temp file when the stream fails before completion', async ()=>{
429
+ const stream = new ReadableStream({
430
+ pull (controller) {
431
+ controller.error(new Error('stream broke'));
432
+ },
433
+ start (controller) {
434
+ controller.enqueue(Buffer.from('partial-chunk'));
435
+ }
436
+ });
437
+ const handler = vi.fn(async ()=>new Response(stream, {
438
+ headers: {
439
+ 'Content-Type': 'video/mp4'
440
+ },
441
+ status: 200
442
+ }));
443
+ const req = createReq({
444
+ handlers: [
445
+ handler
446
+ ],
447
+ upload: {
448
+ disableLocalStorage: true,
449
+ mimeTypes: [
450
+ 'video/*'
451
+ ]
452
+ }
453
+ });
454
+ const file = videoFile();
455
+ const tempFileDir = os.tmpdir();
456
+ const filesBefore = new Set(await fs.readdir(tempFileDir));
457
+ await expect(getFileFromClientUpload({
458
+ file,
459
+ req
460
+ })).rejects.toThrow('stream broke');
461
+ const filesAfter = await fs.readdir(tempFileDir);
462
+ const leftoverTempFiles = filesAfter.filter((entry)=>!filesBefore.has(entry) && entry.startsWith('payload-client-upload-'));
463
+ expect(leftoverTempFiles).toEqual([]);
464
+ });
465
+ it('requires full content and skips the Range header when the request carries crop or size edits', async ()=>{
466
+ const handler = vi.fn(async (handlerReq)=>{
467
+ expect(handlerReq.headers.get('Range')).toBeNull();
468
+ return new Response(MINIMAL_PNG, {
469
+ headers: {
470
+ 'Content-Type': 'image/png'
471
+ },
472
+ status: 200
473
+ });
474
+ });
475
+ const req = createReq({
476
+ handlers: [
477
+ handler
478
+ ],
479
+ upload: {
480
+ disableLocalStorage: true
481
+ }
482
+ });
483
+ req.query = {
484
+ uploadEdits: {
485
+ heightInPixels: 100,
486
+ widthInPixels: 100
487
+ }
488
+ };
489
+ const file = imageFile();
490
+ const result = await getFileFromClientUpload({
491
+ file,
492
+ req
493
+ });
494
+ tempFilesToRemove.push(result.tempFilePath);
495
+ expect(handler).toHaveBeenCalledTimes(1);
496
+ expect(result.tempFilePath).toBeDefined();
497
+ expect(result.data.length).toBe(0);
498
+ });
499
+ it('treats a null response body as a legitimate zero-byte file', async ()=>{
500
+ const handler = vi.fn(async ()=>new Response(null, {
501
+ headers: {
502
+ 'Content-Type': 'video/mp4'
503
+ },
504
+ status: 200
505
+ }));
506
+ const req = createReq({
507
+ handlers: [
508
+ handler
509
+ ],
510
+ upload: {
511
+ disableLocalStorage: true,
512
+ mimeTypes: [
513
+ 'video/*'
514
+ ]
515
+ }
516
+ });
517
+ const file = videoFile({
518
+ size: 0
519
+ });
520
+ const result = await getFileFromClientUpload({
521
+ file,
522
+ req
523
+ });
524
+ tempFilesToRemove.push(result.tempFilePath);
525
+ expect(result.tempFilePath).toBeDefined();
526
+ const written = await fs.readFile(result.tempFilePath);
527
+ expect(written.length).toBe(0);
528
+ });
529
+ it('streams the full file to disk when a non-image client upload carries no context', async ()=>{
530
+ const handler = vi.fn(async ()=>new Response(Buffer.from('full-video-bytes'), {
531
+ headers: {
532
+ 'Content-Type': 'video/mp4'
533
+ },
534
+ status: 200
535
+ }));
536
+ const req = createReq({
537
+ handlers: [
538
+ handler
539
+ ],
540
+ upload: {
541
+ disableLocalStorage: true
542
+ }
543
+ });
544
+ const file = videoFile({
545
+ clientUploadContext: undefined
546
+ });
547
+ const result = await getFileFromClientUpload({
548
+ file,
549
+ req
550
+ });
551
+ expect(handler).toHaveBeenCalledTimes(1);
552
+ expect(result.tempFilePath).toBeDefined();
553
+ tempFilesToRemove.push(result.tempFilePath);
554
+ const written = await fs.readFile(result.tempFilePath);
555
+ expect(written.toString()).toBe('full-video-bytes');
556
+ });
557
+ it('streams the full file to disk when an image client upload carries no context', async ()=>{
558
+ const handler = vi.fn(async (handlerReq)=>{
559
+ expect(handlerReq.headers.get('Range')).toBeNull();
560
+ return new Response(MINIMAL_PNG, {
561
+ headers: {
562
+ 'Content-Type': 'image/png'
563
+ },
564
+ status: 200
565
+ });
566
+ });
567
+ const req = createReq({
568
+ handlers: [
569
+ handler
570
+ ],
571
+ upload: {
572
+ disableLocalStorage: true
573
+ }
574
+ });
575
+ const file = imageFile({
576
+ clientUploadContext: undefined
577
+ });
578
+ const result = await getFileFromClientUpload({
579
+ file,
580
+ req
581
+ });
582
+ expect(handler).toHaveBeenCalledTimes(1);
583
+ expect(result.tempFilePath).toBeDefined();
584
+ tempFilesToRemove.push(result.tempFilePath);
585
+ const written = await fs.readFile(result.tempFilePath);
586
+ expect(written).toEqual(MINIMAL_PNG);
587
+ });
588
+ });
589
+
590
+ //# sourceMappingURL=getFileFromClientUpload.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/uploads/getFileFromClientUpload.spec.ts"],"sourcesContent":["import type { PayloadRequest } from '../types/index.js'\nimport type { SanitizedUploadConfig } from './types.js'\n\nimport fs from 'fs/promises'\nimport os from 'os'\nimport { afterEach, describe, expect, it, vi } from 'vitest'\n\nimport { HEADER_PROBE_BYTE_LENGTH } from './getFileContentRequirement.js'\nimport { type ClientUploadData, getFileFromClientUpload } from './getFileFromClientUpload.js'\nimport { getImageSize } from './getImageSize.js'\n\nconst MINIMAL_PNG = (() => {\n const signature = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])\n const ihdrData = Buffer.alloc(13)\n ihdrData.writeUInt32BE(400, 0) // width\n ihdrData.writeUInt32BE(300, 4) // height\n ihdrData[8] = 8 // bit depth\n ihdrData[9] = 2 // color type: RGB\n const ihdrChunk = Buffer.concat([\n Buffer.from([0, 0, 0, 13]),\n Buffer.from('IHDR'),\n ihdrData,\n Buffer.alloc(4), // crc (unchecked by dimension probing)\n ])\n return Buffer.concat([signature, ihdrChunk])\n})()\n\nconst tempFilesToRemove: string[] = []\n\nafterEach(async () => {\n vi.unstubAllGlobals()\n for (const tempFilePath of tempFilesToRemove) {\n await fs.rm(tempFilePath, { force: true })\n }\n tempFilesToRemove.length = 0\n})\n\nconst createReq = ({\n handlers,\n upload,\n}: {\n handlers: NonNullable<SanitizedUploadConfig['handlers']>\n upload?: Partial<SanitizedUploadConfig>\n}): PayloadRequest => {\n const request = new Request('http://localhost/api/media')\n const req = request as unknown as PayloadRequest\n\n req.query = {}\n req.payload = {\n collections: {\n media: {\n config: {\n upload: {\n disableLocalStorage: true,\n handlers,\n ...upload,\n },\n },\n },\n },\n config: {\n upload: {},\n },\n logger: { error: vi.fn() },\n } as unknown as PayloadRequest['payload']\n\n return req\n}\n\nconst videoFile = (overrides: Partial<ClientUploadData> = {}): ClientUploadData => ({\n clientUploadContext: { prefix: 'abc' },\n collectionSlug: 'media',\n filename: 'clip.mp4',\n mimeType: 'video/mp4',\n size: 10,\n ...overrides,\n})\n\nconst imageFile = (overrides: Partial<ClientUploadData> = {}): ClientUploadData => ({\n clientUploadContext: { prefix: 'abc' },\n collectionSlug: 'media',\n filename: 'photo.png',\n mimeType: 'image/png',\n size: MINIMAL_PNG.length,\n ...overrides,\n})\n\ndescribe('getFileFromClientUpload', () => {\n it('does not call a handler and returns empty data with an own clientUploadContext property when no content is required', async () => {\n const handler = vi.fn()\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = videoFile()\n\n const result = await getFileFromClientUpload({ file, req })\n\n expect(handler).not.toHaveBeenCalled()\n expect(Object.prototype.hasOwnProperty.call(result, 'clientUploadContext')).toBe(true)\n expect(result.clientUploadContext).toEqual({ prefix: 'abc' })\n expect(result.data.length).toBe(0)\n expect(result.tempFilePath).toBeUndefined()\n expect(result.name).toBe('clip.mp4')\n expect(result.size).toBe(10)\n })\n\n it('requests a bounded byte range and returns dimensions for a header-only image', async () => {\n const handler = vi.fn(async (handlerReq: PayloadRequest) => {\n expect(handlerReq.headers.get('Range')).toBe(`bytes=0-${HEADER_PROBE_BYTE_LENGTH - 1}`)\n return new Response(MINIMAL_PNG, {\n headers: { 'Content-Type': 'image/png' },\n status: 206,\n })\n })\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = imageFile()\n\n const result = await getFileFromClientUpload({ file, req })\n\n expect(result.data.length).toBeLessThanOrEqual(HEADER_PROBE_BYTE_LENGTH)\n expect(result.tempFilePath).toBeUndefined()\n await expect(getImageSize({ file: result })).resolves.toEqual({ height: 300, width: 400 })\n })\n\n it('caps the header read at the boundary and cancels the stream without pulling past it', async () => {\n let hasCancelled = false\n let hasReadPastBoundary = false\n const firstChunk = Buffer.concat([\n MINIMAL_PNG,\n Buffer.alloc(HEADER_PROBE_BYTE_LENGTH - MINIMAL_PNG.length),\n ])\n const stream = new ReadableStream(\n {\n cancel() {\n hasCancelled = true\n },\n pull(controller) {\n hasReadPastBoundary = true\n controller.error(new Error('Read past header boundary'))\n },\n start(controller) {\n controller.enqueue(firstChunk)\n },\n },\n { highWaterMark: 0 },\n )\n\n const handler = vi.fn(\n async () => new Response(stream, { headers: { 'Content-Type': 'image/png' }, status: 206 }),\n )\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = imageFile({ size: HEADER_PROBE_BYTE_LENGTH })\n\n const result = await getFileFromClientUpload({ file, req })\n\n expect(result.data.length).toBe(HEADER_PROBE_BYTE_LENGTH)\n expect(hasCancelled).toBe(true)\n expect(hasReadPastBoundary).toBe(false)\n })\n\n it('lets a handler read native Request properties like signal through the Range-scoped proxy', async () => {\n const handler = vi.fn(async (handlerReq: PayloadRequest) => {\n expect(() => handlerReq.signal).not.toThrow()\n expect(handlerReq.signal).toBeInstanceOf(AbortSignal)\n return new Response(MINIMAL_PNG, {\n headers: { 'Content-Type': 'image/png' },\n status: 206,\n })\n })\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = imageFile()\n\n await getFileFromClientUpload({ file, req })\n\n expect(handler).toHaveBeenCalledTimes(1)\n })\n\n it('falls back to a full fetch when the bounded header cannot be probed for dimensions', async () => {\n const handler = vi\n .fn()\n .mockResolvedValueOnce(\n new Response(Buffer.from('not an image'), {\n headers: { 'Content-Type': 'image/png' },\n status: 206,\n }),\n )\n .mockResolvedValueOnce(\n new Response(MINIMAL_PNG, { headers: { 'Content-Type': 'image/png' }, status: 200 }),\n )\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = imageFile()\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(handler).toHaveBeenCalledTimes(2)\n expect(result.tempFilePath).toBeDefined()\n expect(result.data.length).toBe(0)\n })\n\n it('streams a full-content response to a temp file without buffering the whole body', async () => {\n const chunks = [\n Buffer.from('chunk-one-'),\n Buffer.from('chunk-two-'),\n Buffer.from('chunk-three'),\n ]\n const stream = new ReadableStream({\n start(controller) {\n for (const chunk of chunks) {\n controller.enqueue(chunk)\n }\n controller.close()\n },\n })\n\n const response = new Response(stream, {\n headers: { 'Content-Type': 'video/mp4' },\n status: 200,\n })\n const arrayBufferTripwire = vi.fn(async () => {\n throw new Error('Unexpected whole-body buffering')\n })\n Object.defineProperty(response, 'arrayBuffer', { value: arrayBufferTripwire })\n\n const handler = vi.fn(async () => response)\n const req = createReq({\n handlers: [handler],\n upload: { disableLocalStorage: true, mimeTypes: ['video/*'] },\n })\n const file = videoFile({ size: 30 })\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(result.data).toEqual(Buffer.alloc(0))\n expect(arrayBufferTripwire).not.toHaveBeenCalled()\n const written = await fs.readFile(result.tempFilePath!)\n expect(written.toString()).toBe(chunks.map((chunk) => chunk.toString()).join(''))\n })\n\n it('runs every handler and uses the last one that returns a response, matching v3', async () => {\n const firstHandler = vi.fn(async () => new Response(Buffer.from('first-response')))\n const secondHandler = vi.fn(async () => new Response(Buffer.from('second-response')))\n const req = createReq({\n handlers: [firstHandler, secondHandler],\n upload: { disableLocalStorage: true, mimeTypes: ['video/*'] },\n })\n const file = videoFile()\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(firstHandler).toHaveBeenCalledTimes(1)\n expect(secondHandler).toHaveBeenCalledTimes(1)\n const written = await fs.readFile(result.tempFilePath!)\n expect(written.toString()).toBe('second-response')\n })\n\n it('falls through to the next handler when the first returns nothing', async () => {\n const firstHandler = vi.fn(async () => undefined)\n const secondHandler = vi.fn(async () => new Response(Buffer.alloc(10), { status: 200 }))\n const req = createReq({\n handlers: [firstHandler, secondHandler],\n upload: { disableLocalStorage: true, mimeTypes: ['video/*'] },\n })\n const file = videoFile()\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(firstHandler).toHaveBeenCalledTimes(1)\n expect(secondHandler).toHaveBeenCalledTimes(1)\n })\n\n it('follows exactly one redirect from a handler response', async () => {\n const redirectTarget = 'http://storage.example.com/file.mp4'\n const handler = vi.fn(\n async () => new Response(null, { headers: { Location: redirectTarget }, status: 302 }),\n )\n const fetchMock = vi.fn(\n async () =>\n new Response(Buffer.alloc(10), { headers: { 'Content-Type': 'video/mp4' }, status: 200 }),\n )\n vi.stubGlobal('fetch', fetchMock)\n\n const req = createReq({\n handlers: [handler],\n upload: { disableLocalStorage: true, mimeTypes: ['video/*'] },\n })\n const file = videoFile()\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(fetchMock).toHaveBeenCalledTimes(1)\n expect(fetchMock).toHaveBeenCalledWith(redirectTarget)\n })\n\n it('forwards the Range header through a redirect during the bounded header probe', async () => {\n const redirectTarget = 'http://storage.example.com/photo.png'\n const handler = vi.fn(\n async () => new Response(null, { headers: { Location: redirectTarget }, status: 302 }),\n )\n const fetchMock = vi.fn(\n async () =>\n new Response(MINIMAL_PNG, { headers: { 'Content-Type': 'image/png' }, status: 206 }),\n )\n vi.stubGlobal('fetch', fetchMock)\n\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = imageFile()\n\n const result = await getFileFromClientUpload({ file, req })\n\n expect(result.tempFilePath).toBeUndefined()\n expect(fetchMock).toHaveBeenCalledTimes(1)\n expect(fetchMock).toHaveBeenCalledWith(redirectTarget, {\n headers: { Range: `bytes=0-${HEADER_PROBE_BYTE_LENGTH - 1}` },\n })\n })\n\n it('removes the partial temp file when the stream fails before completion', async () => {\n const stream = new ReadableStream({\n pull(controller) {\n controller.error(new Error('stream broke'))\n },\n start(controller) {\n controller.enqueue(Buffer.from('partial-chunk'))\n },\n })\n const handler = vi.fn(\n async () => new Response(stream, { headers: { 'Content-Type': 'video/mp4' }, status: 200 }),\n )\n const req = createReq({\n handlers: [handler],\n upload: { disableLocalStorage: true, mimeTypes: ['video/*'] },\n })\n const file = videoFile()\n\n const tempFileDir = os.tmpdir()\n const filesBefore = new Set(await fs.readdir(tempFileDir))\n\n await expect(getFileFromClientUpload({ file, req })).rejects.toThrow('stream broke')\n\n const filesAfter = await fs.readdir(tempFileDir)\n const leftoverTempFiles = filesAfter.filter(\n (entry) => !filesBefore.has(entry) && entry.startsWith('payload-client-upload-'),\n )\n expect(leftoverTempFiles).toEqual([])\n })\n\n it('requires full content and skips the Range header when the request carries crop or size edits', async () => {\n const handler = vi.fn(async (handlerReq: PayloadRequest) => {\n expect(handlerReq.headers.get('Range')).toBeNull()\n return new Response(MINIMAL_PNG, { headers: { 'Content-Type': 'image/png' }, status: 200 })\n })\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n req.query = { uploadEdits: { heightInPixels: 100, widthInPixels: 100 } }\n const file = imageFile()\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(handler).toHaveBeenCalledTimes(1)\n expect(result.tempFilePath).toBeDefined()\n expect(result.data.length).toBe(0)\n })\n\n it('treats a null response body as a legitimate zero-byte file', async () => {\n const handler = vi.fn(\n async () => new Response(null, { headers: { 'Content-Type': 'video/mp4' }, status: 200 }),\n )\n const req = createReq({\n handlers: [handler],\n upload: { disableLocalStorage: true, mimeTypes: ['video/*'] },\n })\n const file = videoFile({ size: 0 })\n\n const result = await getFileFromClientUpload({ file, req })\n tempFilesToRemove.push(result.tempFilePath!)\n\n expect(result.tempFilePath).toBeDefined()\n const written = await fs.readFile(result.tempFilePath!)\n expect(written.length).toBe(0)\n })\n\n it('streams the full file to disk when a non-image client upload carries no context', async () => {\n const handler = vi.fn(\n async () =>\n new Response(Buffer.from('full-video-bytes'), {\n headers: { 'Content-Type': 'video/mp4' },\n status: 200,\n }),\n )\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = videoFile({ clientUploadContext: undefined })\n\n const result = await getFileFromClientUpload({ file, req })\n\n expect(handler).toHaveBeenCalledTimes(1)\n expect(result.tempFilePath).toBeDefined()\n tempFilesToRemove.push(result.tempFilePath!)\n\n const written = await fs.readFile(result.tempFilePath!)\n expect(written.toString()).toBe('full-video-bytes')\n })\n\n it('streams the full file to disk when an image client upload carries no context', async () => {\n const handler = vi.fn(async (handlerReq: PayloadRequest) => {\n expect(handlerReq.headers.get('Range')).toBeNull()\n return new Response(MINIMAL_PNG, { headers: { 'Content-Type': 'image/png' }, status: 200 })\n })\n const req = createReq({ handlers: [handler], upload: { disableLocalStorage: true } })\n const file = imageFile({ clientUploadContext: undefined })\n\n const result = await getFileFromClientUpload({ file, req })\n\n expect(handler).toHaveBeenCalledTimes(1)\n expect(result.tempFilePath).toBeDefined()\n tempFilesToRemove.push(result.tempFilePath!)\n\n const written = await fs.readFile(result.tempFilePath!)\n expect(written).toEqual(MINIMAL_PNG)\n })\n})\n"],"names":["fs","os","afterEach","describe","expect","it","vi","HEADER_PROBE_BYTE_LENGTH","getFileFromClientUpload","getImageSize","MINIMAL_PNG","signature","Buffer","from","ihdrData","alloc","writeUInt32BE","ihdrChunk","concat","tempFilesToRemove","unstubAllGlobals","tempFilePath","rm","force","length","createReq","handlers","upload","request","Request","req","query","payload","collections","media","config","disableLocalStorage","logger","error","fn","videoFile","overrides","clientUploadContext","prefix","collectionSlug","filename","mimeType","size","imageFile","handler","file","result","not","toHaveBeenCalled","Object","prototype","hasOwnProperty","call","toBe","toEqual","data","toBeUndefined","name","handlerReq","headers","get","Response","status","toBeLessThanOrEqual","resolves","height","width","hasCancelled","hasReadPastBoundary","firstChunk","stream","ReadableStream","cancel","pull","controller","Error","start","enqueue","highWaterMark","signal","toThrow","toBeInstanceOf","AbortSignal","toHaveBeenCalledTimes","mockResolvedValueOnce","push","toBeDefined","chunks","chunk","close","response","arrayBufferTripwire","defineProperty","value","mimeTypes","written","readFile","toString","map","join","firstHandler","secondHandler","undefined","redirectTarget","Location","fetchMock","stubGlobal","toHaveBeenCalledWith","Range","tempFileDir","tmpdir","filesBefore","Set","readdir","rejects","filesAfter","leftoverTempFiles","filter","entry","has","startsWith","toBeNull","uploadEdits","heightInPixels","widthInPixels"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { PayloadRequest } from '../types/index.js';
2
+ type Args = {
3
+ /**
4
+ * A path the caller has already removed, so the redundant unlink is skipped.
5
+ */
6
+ alreadyUnlinkedPath?: string;
7
+ req: PayloadRequest;
8
+ };
9
+ /**
10
+ * Removes the temp file a client upload materialized, if it is still on disk. Logs rather than
11
+ * throws, so cleanup can never replace a response or mask the error that a caller is handling.
12
+ *
13
+ * The path is tracked on `req.context` rather than read back off `req.file` because the file
14
+ * can outlive both: plugin-cloud-storage's afterChange hook clears `req.file` before cleanup
15
+ * runs, and the request parser materializes the file for whatever `collectionSlug` the multipart
16
+ * field names, which does not have to be a collection the endpoint writes a document to.
17
+ */
18
+ export declare const unlinkClientUploadTempFile: (args: Args) => Promise<void>;
19
+ export {};
20
+ //# sourceMappingURL=unlinkClientUploadTempFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlinkClientUploadTempFile.d.ts","sourceRoot":"","sources":["../../src/uploads/unlinkClientUploadTempFile.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAIvD,KAAK,IAAI,GAAG;IACV;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,GAAG,EAAE,cAAc,CAAA;CACpB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAqBpE,CAAA"}