@payloadcms/storage-gcs 4.0.0-internal.cd47d20 → 4.0.0-internal.ce66a41

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.
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAQ/C,UAAU,oBAAoB;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,gBAAgB,EAAE,MAAM,OAAO,CAAA;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,oBAA4B,GAC7B,EAAE,oBAAoB,GAAG,OAAO,CA6DhC"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EACV,OAAO,EACP,mBAAmB,EAEpB,MAAM,wCAAwC,CAAA;AAQ/C,UAAU,oBAAoB;IAC5B,GAAG,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,gBAAgB,EAAE,MAAM,OAAO,CAAA;IAC/B,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,MAAM,EACN,aAAa,EACb,gBAAgB,EAChB,oBAA4B,GAC7B,EAAE,oBAAoB,GAAG,OAAO,CA8DhC"}
package/dist/adapter.js CHANGED
@@ -42,7 +42,7 @@ export function createGcsAdapter({ acl, bucket, clientUploads, getStorageClient,
42
42
  });
43
43
  return data;
44
44
  },
45
- staticHandler: (req, { doc, headers, params: { filename, uploadReference } })=>getFile({
45
+ staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } })=>getFile({
46
46
  bucket,
47
47
  client: getStorageClient(),
48
48
  collection,
@@ -50,6 +50,7 @@ export function createGcsAdapter({ acl, bucket, clientUploads, getStorageClient,
50
50
  doc,
51
51
  filename,
52
52
  incomingHeaders: headers,
53
+ operation,
53
54
  req,
54
55
  uploadReference,
55
56
  useCompositePrefixes
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateUploadInstructions } from './generateUploadInstructions.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateGcsAdapterArgs {\n acl?: 'Private' | 'Public'\n bucket: string\n clientUploads?: ClientUploadsConfig\n getStorageClient: () => Storage\n useCompositePrefixes?: boolean\n}\n\nexport function createGcsAdapter({\n acl,\n bucket,\n clientUploads,\n getStorageClient,\n useCompositePrefixes = false,\n}: CreateGcsAdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'gcs',\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n uploadInstructions: {\n enabled: Boolean(clientUploads),\n generate: generateUploadInstructions({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n bucket,\n collectionPrefix: prefix,\n getStorageClient,\n useCompositePrefixes,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n handleDelete: ({ storageFilePath }) =>\n deleteFile({\n bucket,\n client: getStorageClient(),\n storageFilePath,\n }),\n\n handleUpload: async ({ data, file, storageFilePath }) => {\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n mimeType: file.mimeType,\n storageFilePath,\n })\n\n return data\n },\n\n staticHandler: (req, { doc, headers, params: { filename, uploadReference } }) =>\n getFile({\n bucket,\n client: getStorageClient(),\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n req,\n uploadReference,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","generateUploadInstructions","generateURL","getFile","uploadFile","createGcsAdapter","acl","bucket","clientUploads","getStorageClient","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","client","collectionPrefix","uploadInstructions","enabled","Boolean","generate","access","undefined","requiresUploadReceipt","useInAdmin","handleDelete","storageFilePath","handleUpload","data","file","buffer","mimeType","staticHandler","req","doc","headers","params","uploadReference","incomingHeaders"],"mappings":"AAOA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,0BAA0B,QAAQ,kCAAiC;AAC5E,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,UAAU,QAAQ,kBAAiB;AAU5C,OAAO,SAASC,iBAAiB,EAC/BC,GAAG,EACHC,MAAM,EACNC,aAAa,EACbC,gBAAgB,EAChBC,uBAAuB,KAAK,EACP;IACrB,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YAENX,aAAa,CAAC,EAAEY,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDb,YAAY;oBACVK;oBACAS,QAAQP;oBACRQ,kBAAkBL;oBAClBE;oBACAF,QAAQG;oBACRL;gBACF;YAEFQ,oBAAoB;gBAClBC,SAASC,QAAQZ;gBACjBa,UAAUpB,2BAA2B;oBACnCqB,QAAQ,OAAOd,kBAAkB,WAAWA,cAAcc,MAAM,GAAGC;oBACnEhB;oBACAU,kBAAkBL;oBAClBH;oBACAC;gBACF;gBACAc,uBAAuB;gBACvBC,YAAY;YACd;YAEAC,cAAc,CAAC,EAAEC,eAAe,EAAE,GAChC3B,WAAW;oBACTO;oBACAS,QAAQP;oBACRkB;gBACF;YAEFC,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAEH,eAAe,EAAE;gBAClD,MAAMvB,WAAW;oBACfE;oBACAC;oBACAwB,QAAQD,KAAKC,MAAM;oBACnBf,QAAQP;oBACRuB,UAAUF,KAAKE,QAAQ;oBACvBL;gBACF;gBAEA,OAAOE;YACT;YAEAI,eAAe,CAACC,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAEvB,QAAQ,EAAEwB,eAAe,EAAE,EAAE,GAC1EnC,QAAQ;oBACNI;oBACAS,QAAQP;oBACRE;oBACAM,kBAAkBL;oBAClBuB;oBACArB;oBACAyB,iBAAiBH;oBACjBF;oBACAI;oBACA5B;gBACF;QACJ,CAAA;AACF"}
1
+ {"version":3,"sources":["../src/adapter.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type {\n Adapter,\n ClientUploadsConfig,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\n\nimport { deleteFile } from './deleteFile.js'\nimport { generateUploadInstructions } from './generateUploadInstructions.js'\nimport { generateURL } from './generateURL.js'\nimport { getFile } from './getFile.js'\nimport { uploadFile } from './uploadFile.js'\n\ninterface CreateGcsAdapterArgs {\n acl?: 'Private' | 'Public'\n bucket: string\n clientUploads?: ClientUploadsConfig\n getStorageClient: () => Storage\n useCompositePrefixes?: boolean\n}\n\nexport function createGcsAdapter({\n acl,\n bucket,\n clientUploads,\n getStorageClient,\n useCompositePrefixes = false,\n}: CreateGcsAdapterArgs): Adapter {\n return ({ collection, prefix = '' }): GeneratedAdapter => ({\n name: 'gcs',\n\n generateURL: ({ filename, prefix: urlPrefix = '' }) =>\n generateURL({\n bucket,\n client: getStorageClient(),\n collectionPrefix: prefix,\n filename,\n prefix: urlPrefix,\n useCompositePrefixes,\n }),\n\n uploadInstructions: {\n enabled: Boolean(clientUploads),\n generate: generateUploadInstructions({\n access: typeof clientUploads === 'object' ? clientUploads.access : undefined,\n bucket,\n collectionPrefix: prefix,\n getStorageClient,\n useCompositePrefixes,\n }),\n requiresUploadReceipt: true,\n useInAdmin: true,\n },\n\n handleDelete: ({ storageFilePath }) =>\n deleteFile({\n bucket,\n client: getStorageClient(),\n storageFilePath,\n }),\n\n handleUpload: async ({ data, file, storageFilePath }) => {\n await uploadFile({\n acl,\n bucket,\n buffer: file.buffer,\n client: getStorageClient(),\n mimeType: file.mimeType,\n storageFilePath,\n })\n\n return data\n },\n\n staticHandler: (req, { doc, headers, params: { filename, operation, uploadReference } }) =>\n getFile({\n bucket,\n client: getStorageClient(),\n collection,\n collectionPrefix: prefix,\n doc,\n filename,\n incomingHeaders: headers,\n operation,\n req,\n uploadReference,\n useCompositePrefixes,\n }),\n })\n}\n"],"names":["deleteFile","generateUploadInstructions","generateURL","getFile","uploadFile","createGcsAdapter","acl","bucket","clientUploads","getStorageClient","useCompositePrefixes","collection","prefix","name","filename","urlPrefix","client","collectionPrefix","uploadInstructions","enabled","Boolean","generate","access","undefined","requiresUploadReceipt","useInAdmin","handleDelete","storageFilePath","handleUpload","data","file","buffer","mimeType","staticHandler","req","doc","headers","params","operation","uploadReference","incomingHeaders"],"mappings":"AAOA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,0BAA0B,QAAQ,kCAAiC;AAC5E,SAASC,WAAW,QAAQ,mBAAkB;AAC9C,SAASC,OAAO,QAAQ,eAAc;AACtC,SAASC,UAAU,QAAQ,kBAAiB;AAU5C,OAAO,SAASC,iBAAiB,EAC/BC,GAAG,EACHC,MAAM,EACNC,aAAa,EACbC,gBAAgB,EAChBC,uBAAuB,KAAK,EACP;IACrB,OAAO,CAAC,EAAEC,UAAU,EAAEC,SAAS,EAAE,EAAE,GAAwB,CAAA;YACzDC,MAAM;YAENX,aAAa,CAAC,EAAEY,QAAQ,EAAEF,QAAQG,YAAY,EAAE,EAAE,GAChDb,YAAY;oBACVK;oBACAS,QAAQP;oBACRQ,kBAAkBL;oBAClBE;oBACAF,QAAQG;oBACRL;gBACF;YAEFQ,oBAAoB;gBAClBC,SAASC,QAAQZ;gBACjBa,UAAUpB,2BAA2B;oBACnCqB,QAAQ,OAAOd,kBAAkB,WAAWA,cAAcc,MAAM,GAAGC;oBACnEhB;oBACAU,kBAAkBL;oBAClBH;oBACAC;gBACF;gBACAc,uBAAuB;gBACvBC,YAAY;YACd;YAEAC,cAAc,CAAC,EAAEC,eAAe,EAAE,GAChC3B,WAAW;oBACTO;oBACAS,QAAQP;oBACRkB;gBACF;YAEFC,cAAc,OAAO,EAAEC,IAAI,EAAEC,IAAI,EAAEH,eAAe,EAAE;gBAClD,MAAMvB,WAAW;oBACfE;oBACAC;oBACAwB,QAAQD,KAAKC,MAAM;oBACnBf,QAAQP;oBACRuB,UAAUF,KAAKE,QAAQ;oBACvBL;gBACF;gBAEA,OAAOE;YACT;YAEAI,eAAe,CAACC,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,QAAQ,EAAEvB,QAAQ,EAAEwB,SAAS,EAAEC,eAAe,EAAE,EAAE,GACrFpC,QAAQ;oBACNI;oBACAS,QAAQP;oBACRE;oBACAM,kBAAkBL;oBAClBuB;oBACArB;oBACA0B,iBAAiBJ;oBACjBE;oBACAJ;oBACAK;oBACA7B;gBACF;QACJ,CAAA;AACF"}
package/dist/getFile.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Storage } from '@google-cloud/storage';
2
- import type { CollectionConfig, PayloadRequest, TypeWithID } from 'payload';
2
+ import type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload';
3
3
  interface GetFileArgs {
4
4
  bucket: string;
5
5
  client: Storage;
@@ -8,10 +8,11 @@ interface GetFileArgs {
8
8
  doc?: TypeWithID;
9
9
  filename: string;
10
10
  incomingHeaders?: Headers;
11
+ operation?: FileHandlerOperation;
11
12
  req: PayloadRequest;
12
13
  uploadReference?: unknown;
13
14
  useCompositePrefixes?: boolean;
14
15
  }
15
- export declare function getFile({ bucket, client, collection, collectionPrefix, doc, filename, incomingHeaders, req, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
16
+ export declare function getFile({ bucket, client, collection, collectionPrefix, doc, filename, incomingHeaders, operation, req, uploadReference, useCompositePrefixes, }: GetFileArgs): Promise<Response>;
16
17
  export {};
17
18
  //# sourceMappingURL=getFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAS3E,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,UAAU,EACV,gBAAqB,EACrB,GAAG,EACH,QAAQ,EACR,eAAe,EACf,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmGjC"}
1
+ {"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../src/getFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AASjG,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,EAAE,gBAAgB,CAAA;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,GAAG,CAAC,EAAE,UAAU,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,SAAS,CAAC,EAAE,oBAAoB,CAAA;IAChC,GAAG,EAAE,cAAc,CAAA;IACnB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,wBAAsB,OAAO,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,UAAU,EACV,gBAAqB,EACrB,GAAG,EACH,QAAQ,EACR,eAAe,EACf,SAAkB,EAClB,GAAG,EACH,eAAe,EACf,oBAA4B,GAC7B,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgIjC"}
package/dist/getFile.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { ApiError } from '@google-cloud/storage';
2
2
  import { buildStoragePathData, getFilePrefix as getDocPrefix } from '@payloadcms/plugin-cloud-storage/utilities';
3
3
  import { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal';
4
- export async function getFile({ bucket, client, collection, collectionPrefix = '', doc, filename, incomingHeaders, req, uploadReference, useCompositePrefixes = false }) {
4
+ export async function getFile({ bucket, client, collection, collectionPrefix = '', doc, filename, incomingHeaders, operation = 'read', req, uploadReference, useCompositePrefixes = false }) {
5
+ const isTransformSource = operation === 'transform';
5
6
  try {
6
7
  const docPrefix = await getDocPrefix({
7
8
  collection,
@@ -20,8 +21,7 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
20
21
  });
21
22
  const file = client.bucket(bucket).file(storageFilePath);
22
23
  const [metadata] = await file.getMetadata();
23
- // Handle range request
24
- const rangeHeader = req.headers.get('range');
24
+ const rangeHeader = isTransformSource ? null : req.headers.get('range');
25
25
  const fileSize = Number(metadata.size);
26
26
  const rangeResult = getRangeRequestInfo({
27
27
  fileSize,
@@ -36,7 +36,6 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
36
36
  const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match');
37
37
  const objectEtag = metadata.etag;
38
38
  let headers = new Headers(incomingHeaders);
39
- // Add range-related headers from the result
40
39
  for (const [key, value] of Object.entries(rangeResult.headers)){
41
40
  headers.append(key, value);
42
41
  }
@@ -46,32 +45,57 @@ export async function getFile({ bucket, client, collection, collectionPrefix = '
46
45
  if (isXmlMimeType(metadata.contentType)) {
47
46
  headers.append('Content-Security-Policy', uploadContentSecurityPolicy);
48
47
  }
49
- if (collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
48
+ if (!isTransformSource && collection.upload && typeof collection.upload === 'object' && typeof collection.upload.modifyResponseHeaders === 'function') {
50
49
  headers = collection.upload.modifyResponseHeaders({
51
50
  headers
52
51
  }) || headers;
53
52
  }
54
- if (etagFromHeaders && etagFromHeaders === objectEtag) {
53
+ if (!isTransformSource && etagFromHeaders && etagFromHeaders === objectEtag) {
55
54
  return new Response(null, {
56
55
  headers,
57
56
  status: 304
58
57
  });
59
58
  }
60
- // Manually create a ReadableStream for the web from a Node.js stream.
59
+ const streamOptions = rangeResult.type === 'partial' ? {
60
+ end: rangeResult.rangeEnd,
61
+ start: rangeResult.rangeStart
62
+ } : {};
63
+ const nodeStream = file.createReadStream(streamOptions);
64
+ // A consumer that stops reading (a failed transform, a disconnected client) must end the
65
+ // GCS download too, otherwise the underlying connection stays open until it drains.
66
+ let onAbort;
67
+ const removeAbortListener = ()=>{
68
+ if (onAbort) {
69
+ req.signal?.removeEventListener('abort', onAbort);
70
+ }
71
+ };
72
+ const stopReading = ()=>{
73
+ removeAbortListener();
74
+ nodeStream.destroy();
75
+ };
61
76
  const readableStream = new ReadableStream({
77
+ cancel: stopReading,
62
78
  start (controller) {
63
- const streamOptions = rangeResult.type === 'partial' ? {
64
- end: rangeResult.rangeEnd,
65
- start: rangeResult.rangeStart
66
- } : {};
67
- const nodeStream = file.createReadStream(streamOptions);
79
+ onAbort = ()=>{
80
+ stopReading();
81
+ controller.error(req.signal?.reason);
82
+ };
83
+ if (req.signal?.aborted) {
84
+ onAbort();
85
+ return;
86
+ }
87
+ req.signal?.addEventListener('abort', onAbort, {
88
+ once: true
89
+ });
68
90
  nodeStream.on('data', (chunk)=>{
69
91
  controller.enqueue(new Uint8Array(chunk));
70
92
  });
71
93
  nodeStream.on('end', ()=>{
94
+ removeAbortListener();
72
95
  controller.close();
73
96
  });
74
97
  nodeStream.on('error', (err)=>{
98
+ removeAbortListener();
75
99
  controller.error(err);
76
100
  });
77
101
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type { CollectionConfig, PayloadRequest, TypeWithID } from 'payload'\n\nimport { ApiError } from '@google-cloud/storage'\nimport {\n buildStoragePathData,\n getFilePrefix as getDocPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal'\n\ninterface GetFileArgs {\n bucket: string\n client: Storage\n collection: CollectionConfig\n collectionPrefix?: string\n doc?: TypeWithID\n filename: string\n incomingHeaders?: Headers\n req: PayloadRequest\n uploadReference?: unknown\n useCompositePrefixes?: boolean\n}\n\nexport async function getFile({\n bucket,\n client,\n collection,\n collectionPrefix = '',\n doc,\n filename,\n incomingHeaders,\n req,\n uploadReference,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n try {\n const docPrefix = await getDocPrefix({\n collection,\n collectionPrefix,\n doc,\n filename,\n req,\n uploadReference,\n useCompositePrefixes,\n })\n\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n const file = client.bucket(bucket).file(storageFilePath)\n\n const [metadata] = await file.getMetadata()\n\n // Handle range request\n const rangeHeader = req.headers.get('range')\n const fileSize = Number(metadata.size)\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = metadata.etag\n\n let headers = new Headers(incomingHeaders)\n\n // Add range-related headers from the result\n for (const [key, value] of Object.entries(rangeResult.headers)) {\n headers.append(key, value)\n }\n\n headers.append('Content-Type', String(metadata.contentType))\n headers.append('ETag', String(metadata.etag))\n\n // Apply a restrictive policy to XML-family responses served through Payload.\n if (isXmlMimeType(metadata.contentType)) {\n headers.append('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n\n if (\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (etagFromHeaders && etagFromHeaders === objectEtag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n // Manually create a ReadableStream for the web from a Node.js stream.\n const readableStream = new ReadableStream({\n start(controller) {\n const streamOptions =\n rangeResult.type === 'partial'\n ? { end: rangeResult.rangeEnd, start: rangeResult.rangeStart }\n : {}\n const nodeStream = file.createReadStream(streamOptions)\n nodeStream.on('data', (chunk) => {\n controller.enqueue(new Uint8Array(chunk))\n })\n nodeStream.on('end', () => {\n controller.close()\n })\n nodeStream.on('error', (err) => {\n controller.error(err)\n })\n },\n })\n\n return new Response(readableStream, {\n headers,\n status: rangeResult.status,\n })\n } catch (err: unknown) {\n if (err instanceof ApiError && err.code === 404) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n req.payload.logger.error(err)\n return new Response('Internal Server Error', { status: 500 })\n }\n}\n"],"names":["ApiError","buildStoragePathData","getFilePrefix","getDocPrefix","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","getFile","bucket","client","collection","collectionPrefix","doc","filename","incomingHeaders","req","uploadReference","useCompositePrefixes","docPrefix","storageFilePath","file","metadata","getMetadata","rangeHeader","headers","get","fileSize","Number","size","rangeResult","type","Response","Headers","status","etagFromHeaders","objectEtag","etag","key","value","Object","entries","append","String","contentType","upload","modifyResponseHeaders","readableStream","ReadableStream","start","controller","streamOptions","end","rangeEnd","rangeStart","nodeStream","createReadStream","on","chunk","enqueue","Uint8Array","close","err","error","code","statusText","payload","logger"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/getFile.ts"],"sourcesContent":["import type { Storage } from '@google-cloud/storage'\nimport type { CollectionConfig, FileHandlerOperation, PayloadRequest, TypeWithID } from 'payload'\n\nimport { ApiError } from '@google-cloud/storage'\nimport {\n buildStoragePathData,\n getFilePrefix as getDocPrefix,\n} from '@payloadcms/plugin-cloud-storage/utilities'\nimport { getRangeRequestInfo, isXmlMimeType, uploadContentSecurityPolicy } from 'payload/internal'\n\ninterface GetFileArgs {\n bucket: string\n client: Storage\n collection: CollectionConfig\n collectionPrefix?: string\n doc?: TypeWithID\n filename: string\n incomingHeaders?: Headers\n operation?: FileHandlerOperation\n req: PayloadRequest\n uploadReference?: unknown\n useCompositePrefixes?: boolean\n}\n\nexport async function getFile({\n bucket,\n client,\n collection,\n collectionPrefix = '',\n doc,\n filename,\n incomingHeaders,\n operation = 'read',\n req,\n uploadReference,\n useCompositePrefixes = false,\n}: GetFileArgs): Promise<Response> {\n const isTransformSource = operation === 'transform'\n\n try {\n const docPrefix = await getDocPrefix({\n collection,\n collectionPrefix,\n doc,\n filename,\n req,\n uploadReference,\n useCompositePrefixes,\n })\n\n const { storageFilePath } = buildStoragePathData({\n collectionPrefix,\n docPrefix,\n filename,\n useCompositePrefixes,\n })\n\n const file = client.bucket(bucket).file(storageFilePath)\n\n const [metadata] = await file.getMetadata()\n\n const rangeHeader = isTransformSource ? null : req.headers.get('range')\n const fileSize = Number(metadata.size)\n const rangeResult = getRangeRequestInfo({ fileSize, rangeHeader })\n\n if (rangeResult.type === 'invalid') {\n return new Response(null, {\n headers: new Headers(rangeResult.headers),\n status: rangeResult.status,\n })\n }\n\n const etagFromHeaders = req.headers.get('etag') || req.headers.get('if-none-match')\n const objectEtag = metadata.etag\n\n let headers = new Headers(incomingHeaders)\n\n for (const [key, value] of Object.entries(rangeResult.headers)) {\n headers.append(key, value)\n }\n\n headers.append('Content-Type', String(metadata.contentType))\n headers.append('ETag', String(metadata.etag))\n\n // Apply a restrictive policy to XML-family responses served through Payload.\n if (isXmlMimeType(metadata.contentType)) {\n headers.append('Content-Security-Policy', uploadContentSecurityPolicy)\n }\n\n if (\n !isTransformSource &&\n collection.upload &&\n typeof collection.upload === 'object' &&\n typeof collection.upload.modifyResponseHeaders === 'function'\n ) {\n headers = collection.upload.modifyResponseHeaders({ headers }) || headers\n }\n\n if (!isTransformSource && etagFromHeaders && etagFromHeaders === objectEtag) {\n return new Response(null, {\n headers,\n status: 304,\n })\n }\n\n const streamOptions =\n rangeResult.type === 'partial'\n ? { end: rangeResult.rangeEnd, start: rangeResult.rangeStart }\n : {}\n const nodeStream = file.createReadStream(streamOptions)\n\n // A consumer that stops reading (a failed transform, a disconnected client) must end the\n // GCS download too, otherwise the underlying connection stays open until it drains.\n let onAbort: (() => void) | undefined\n const removeAbortListener = () => {\n if (onAbort) {\n req.signal?.removeEventListener('abort', onAbort)\n }\n }\n const stopReading = () => {\n removeAbortListener()\n nodeStream.destroy()\n }\n\n const readableStream = new ReadableStream<Uint8Array>({\n cancel: stopReading,\n start(controller) {\n onAbort = () => {\n stopReading()\n controller.error(req.signal?.reason)\n }\n\n if (req.signal?.aborted) {\n onAbort()\n return\n }\n\n req.signal?.addEventListener('abort', onAbort, { once: true })\n\n nodeStream.on('data', (chunk) => {\n controller.enqueue(new Uint8Array(chunk))\n })\n nodeStream.on('end', () => {\n removeAbortListener()\n controller.close()\n })\n nodeStream.on('error', (err) => {\n removeAbortListener()\n controller.error(err)\n })\n },\n })\n\n return new Response(readableStream, {\n headers,\n status: rangeResult.status,\n })\n } catch (err: unknown) {\n if (err instanceof ApiError && err.code === 404) {\n return new Response(null, { status: 404, statusText: 'Not Found' })\n }\n req.payload.logger.error(err)\n return new Response('Internal Server Error', { status: 500 })\n }\n}\n"],"names":["ApiError","buildStoragePathData","getFilePrefix","getDocPrefix","getRangeRequestInfo","isXmlMimeType","uploadContentSecurityPolicy","getFile","bucket","client","collection","collectionPrefix","doc","filename","incomingHeaders","operation","req","uploadReference","useCompositePrefixes","isTransformSource","docPrefix","storageFilePath","file","metadata","getMetadata","rangeHeader","headers","get","fileSize","Number","size","rangeResult","type","Response","Headers","status","etagFromHeaders","objectEtag","etag","key","value","Object","entries","append","String","contentType","upload","modifyResponseHeaders","streamOptions","end","rangeEnd","start","rangeStart","nodeStream","createReadStream","onAbort","removeAbortListener","signal","removeEventListener","stopReading","destroy","readableStream","ReadableStream","cancel","controller","error","reason","aborted","addEventListener","once","on","chunk","enqueue","Uint8Array","close","err","code","statusText","payload","logger"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payloadcms/storage-gcs",
3
- "version": "4.0.0-internal.cd47d20",
3
+ "version": "4.0.0-internal.ce66a41",
4
4
  "description": "Payload storage adapter for Google Cloud Storage",
5
5
  "homepage": "https://payloadcms.com",
6
6
  "repository": {
@@ -33,13 +33,13 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "@google-cloud/storage": "7.19.0",
36
- "@payloadcms/plugin-cloud-storage": "4.0.0-internal.cd47d20"
36
+ "@payloadcms/plugin-cloud-storage": "4.0.0-internal.ce66a41"
37
37
  },
38
38
  "devDependencies": {
39
- "payload": "4.0.0-internal.cd47d20"
39
+ "payload": "4.0.0-internal.ce66a41"
40
40
  },
41
41
  "peerDependencies": {
42
- "payload": "4.0.0-internal.cd47d20"
42
+ "payload": "4.0.0-internal.ce66a41"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=24.15.0"