diff --git a/.changeset/tidy-items-fry.md b/.changeset/tidy-items-fry.md new file mode 100644 index 00000000..4898ca2c --- /dev/null +++ b/.changeset/tidy-items-fry.md @@ -0,0 +1,5 @@ +--- +"@tus/server": minor +--- + +Introduce `exposedHeaders` option for custom Access-Control-Expose-Headers diff --git a/packages/server/README.md b/packages/server/README.md index a29f2a3f..714075e6 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -98,6 +98,10 @@ Allow `Forwarded`, `X-Forwarded-Proto`, and `X-Forwarded-Host` headers to overri Additional headers sent in `Access-Control-Allow-Headers` (`string[]`). +#### `options.exposedHeaders` + +Additional headers sent in `Access-Control-Expose-Headers` (`string[]`). + #### `options.generateUrl` Control how the upload URL is generated (`(req, { proto, host, path, id }) => string)`) diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 6b6a3ca5..572caca8 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -4,7 +4,7 @@ import {EventEmitter} from 'node:events' import type {ServerRequest} from 'srvx/types' import {toNodeHandler} from 'srvx/node' import debug from 'debug' -import {EVENTS, ERRORS, EXPOSED_HEADERS, REQUEST_METHODS, TUS_RESUMABLE} from '@tus/utils' +import {EVENTS, ERRORS, REQUEST_METHODS, TUS_RESUMABLE, HEADERS} from '@tus/utils' import type {DataStore, Upload, CancellationContext} from '@tus/utils' import {GetHandler} from './handlers/GetHandler.js' @@ -200,7 +200,10 @@ export class Server extends EventEmitter { 'Access-Control-Allow-Origin', this.getCorsOrigin(req.headers.get('origin')) ) - headers.set('Access-Control-Expose-Headers', EXPOSED_HEADERS) + headers.set( + 'Access-Control-Expose-Headers', + [...HEADERS, this.options.exposedHeaders ?? []].join(', ') + ) if (this.options.allowedCredentials === true) { headers.set('Access-Control-Allow-Credentials', 'true') diff --git a/packages/server/src/test/Server.test.ts b/packages/server/src/test/Server.test.ts index 42fddafa..4aac0344 100644 --- a/packages/server/src/test/Server.test.ts +++ b/packages/server/src/test/Server.test.ts @@ -163,6 +163,19 @@ describe('Server', () => { }) }) + it('OPTIONS should return returns custom headers in Access-Control-Expose-Headers', (done) => { + server.options.exposedHeaders = ['Custom-Header'] + + request(listener) + .options('/') + .expect(204, '', (err, res) => { + res.headers.should.have.property('access-control-expose-headers') + res.headers['access-control-expose-headers'].should.containEql('Custom-Header') + server.options.exposedHeaders = [] + done(err) + }) + }) + it('OPTIONS should return returns custom headers in Access-Control-Allow-Credentials', (done) => { server.options.allowedCredentials = true diff --git a/packages/server/src/types.ts b/packages/server/src/types.ts index d98430d8..5d6ace18 100644 --- a/packages/server/src/types.ts +++ b/packages/server/src/types.ts @@ -31,6 +31,11 @@ export type ServerOptions = { */ allowedHeaders?: string[] + /** + * Additional headers sent in `Access-Control-Expose-Headers`. + */ + exposedHeaders?: string[] + /** * Set `Access-Control-Allow-Credentials` to true or false (the default) */