diff --git a/package-lock.json b/package-lock.json index 02d9593f..784065a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2625,6 +2625,11 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3493,8 +3498,7 @@ "buffer-from": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" }, "buffer-xor": { "version": "1.0.3", @@ -5243,8 +5247,7 @@ "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" }, "diff-sequences": { "version": "24.9.0", @@ -6098,6 +6101,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-xml-parser": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.16.0.tgz", + "integrity": "sha512-U+bpScacfgnfNfIKlWHDu4u6rtOaCyxhblOLJ8sZPkhsjgGqdZmVPBhdOyvdMGCDt8CsAv+cssOP3NzQptNt2w==" + }, "fastq": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", @@ -12284,6 +12292,11 @@ "semver": "^5.6.0" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -16873,6 +16886,34 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", "dev": true }, + "ts-node": { + "version": "8.10.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.1.tgz", + "integrity": "sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==", + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, "tslib": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", @@ -18284,6 +18325,11 @@ "requires": { "camelcase": "^3.0.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" } } } diff --git a/package.json b/package.json index b73a1726..0c56d903 100644 --- a/package.json +++ b/package.json @@ -46,13 +46,15 @@ "abab": "^2.0.3", "debug": "^4.1.1", "eventemitter3": "^4.0.0", + "fast-xml-parser": "^3.16.0", "file-type": "^10.11.0", "follow-redirects": "^1.10.0", "isutf8": "^2.1.0", "jsonschema": "^1.2.5", "lodash.clonedeep": "^4.5.0", "p-queue": "^4.0.0", - "spark-md5": "^3.0.0" + "spark-md5": "^3.0.0", + "ts-node": "^8.10.1" }, "devDependencies": { "@babel/core": "^7.8.4", diff --git a/src/lib/api/prefetch.spec.browser.ts b/src/lib/api/prefetch.spec.browser.ts index 15678770..2d29a898 100644 --- a/src/lib/api/prefetch.spec.browser.ts +++ b/src/lib/api/prefetch.spec.browser.ts @@ -225,45 +225,4 @@ describe('Prefetch', () => { scope.done(); }); - - it('should send only events when config is already prefetched', async () => { - const sessionCopy = { ...testSession }; - const mockPref = jest.fn() .mockImplementation(() => ({})); - - scope.post('/prefetch').twice().reply(200, (_, data) => mockPref(data)); - - const prefetch = new Prefetch(sessionCopy); - const toSend = { - events: [PrefetchEvents.PICKER], - pickerOptions: { - uploadInBackground: true, - }, - }; - - await prefetch.getConfig(toSend); - - // add one more options request for second request - scope - .options(/.*/) - .reply(204); - - await prefetch.getConfig(toSend); - - expect(mockPref).toHaveBeenCalledTimes(2); - expect(mockPref).toHaveBeenCalledWith({ - apikey: testApiKey, - events: [PrefetchEvents.PICKER], - settings: ['inapp_browser'], - picker_config: { - uploadInBackground: true, - }, - }); - - expect(mockPref).toHaveBeenCalledWith({ - apikey: testApiKey, - events: [PrefetchEvents.PICKER], - }); - - scope.done(); - }); }); diff --git a/src/lib/api/prefetch.ts b/src/lib/api/prefetch.ts index a6f2d039..b23b162f 100644 --- a/src/lib/api/prefetch.ts +++ b/src/lib/api/prefetch.ts @@ -93,9 +93,9 @@ export class Prefetch { paramsToSend.security = { policy: this.session.policy, signature: this.session.signature }; } - if (this.session.prefetch && events) { - return FsRequest.post(`${this.session.urls.uploadApiUrl}/prefetch`, { ...paramsToSend, events }).then(() => this.session.prefetch); - } + // if (this.session.prefetch && events) { + // return FsRequest.post(`${this.session.urls.uploadApiUrl}/prefetch`, { ...paramsToSend, events }).then(() => this.session.prefetch); + // } // we should always ask for this setting for picker if (!settings) { @@ -103,7 +103,7 @@ export class Prefetch { } else { settings = settings.concat(['inapp_browser']); // make arrray unique - settings = settings.filter((v, i, a) => settings.indexOf(v) === i); + settings = settings.filter((v, i) => settings.indexOf(v) === i); } let pickerOptionsToSend; diff --git a/src/lib/api/upload/file_tools.browser.ts b/src/lib/api/upload/file_tools.browser.ts index ac5e0cc0..dc93421e 100644 --- a/src/lib/api/upload/file_tools.browser.ts +++ b/src/lib/api/upload/file_tools.browser.ts @@ -178,7 +178,7 @@ export const getFile = (input: InputFile, sanitizeOptions?: SanitizeOptions): Pr return readFile(file).then( async res => { let mime = file.type; - if (!file.type) { + if (!file.type || file.type.length === 0 || file.type === 'text/plain') { mime = getMimetype(await res.slice(0, fileType.minimumBytes), filename); } diff --git a/src/lib/api/upload/uploaders/s3.spec.ts b/src/lib/api/upload/uploaders/s3.spec.ts index f21a2c27..494a58ed 100644 --- a/src/lib/api/upload/uploaders/s3.spec.ts +++ b/src/lib/api/upload/uploaders/s3.spec.ts @@ -51,6 +51,15 @@ const getSmallTestFile = () => name: 'test.txt', }); +const getNullSizeFile = () => + new File({ + slice: (start, end) => Promise.resolve(testBuff.slice(start, end)), + type: 'text/plain', + // @ts-ignore + size: 0, + name: 'test.txt', + }); + const testApikey = 'testapikey'; const testHost = 'https://filestack-test.com'; const mockUploadId = '123132123'; @@ -230,6 +239,25 @@ describe('Api/Upload/Uploaders/S3', () => { expect(mockPut).toHaveBeenCalled(); }); + it('should throw error on file size 0', async () => { + mockStart.mockReturnValue({ + uri: mockedUri, + region: mockRegion, + upload_id: mockUploadId, + location_url: testHost.replace('https://', ''), + }); + + const u = new S3Uploader({}); + u.setUrl(testHost); + u.setApikey(testApikey); + u.addFile(getNullSizeFile()); + + const res = await u.execute(); + + expect(res[0].status).toEqual('Failed'); + expect(mockStart).not.toHaveBeenCalled(); + }); + it('should throw error on wrong etag field', async (done) => { mockStart.mockReturnValue({ uri: mockedUri, diff --git a/src/lib/api/upload/uploaders/s3.ts b/src/lib/api/upload/uploaders/s3.ts index c44950f1..167efea5 100644 --- a/src/lib/api/upload/uploaders/s3.ts +++ b/src/lib/api/upload/uploaders/s3.ts @@ -286,6 +286,11 @@ export class S3Uploader extends UploaderAbstract { private startRequest(id: string): Promise { const payload = this.getPayloadById(id); + if (payload.file.size === 0) { + this.setPayloadStatus(id, FileState.FAILED); + return Promise.reject(new FilestackError(`Invalid file "${payload.file.name}" size - 0`, {}, FilestackErrorType.VALIDATION)); + } + debug(`[${id}] Make start request`); return FsRequest.post( `${this.getUrl()}/multipart/start`, @@ -435,7 +440,6 @@ export class S3Uploader extends UploaderAbstract { const { data, headers } = await this.getS3PartMetadata(id, part); debug(`[${id}] Received part ${partNumber} info body: \n%O\n headers: \n%O\n`, data, headers); - return FsRequest.put(data.url, part.buffer, { cancelToken: this.cancelToken, timeout: this.timeout, @@ -465,6 +469,26 @@ export class S3Uploader extends UploaderAbstract { return res; }) .catch(err => { + const resp = err && err.response ? err.response : null; + + /* istanbul ignore next */ + if (resp && resp.status === 403) { + if (resp.data && resp.data.Error && resp.data.Error.code) { + let code = resp.data.Error.code; + + if (Array.isArray(code)) { + code = code.pop(); + } + + switch (code) { + case 'RequestTimeTooSkewed': + return this.startPart(id, partNumber); + default: + return Promise.reject(new FilestackError('Cannot upload file', resp.data.Error, FilestackErrorType.REQUEST)); + } + } + } + // release memory part = null; @@ -554,6 +578,25 @@ export class S3Uploader extends UploaderAbstract { return this.uploadNextChunk(id, partNumber, chunkSize); }) .catch(err => { + const resp = err && err.response ? err.response : null; + /* istanbul ignore next */ + if (resp && resp.status === 403) { + if (resp.data && resp.data.Error && resp.data.Error.code) { + let code = resp.data.Error.code; + + if (Array.isArray(code)) { + code = code.pop(); + } + + switch (code) { + case 'RequestTimeTooSkewed': + return this.startPart(id, partNumber); + default: + return Promise.reject(new FilestackError('Cannot upload file', resp.data.Error, FilestackErrorType.REQUEST)); + } + } + } + // reset progress on failed upload this.onProgressUpdate(id, partNumber, part.offset); const nextChunkSize = chunkSize / 2; diff --git a/src/lib/request/adapters/http.ts b/src/lib/request/adapters/http.ts index c84a3263..56dbe287 100644 --- a/src/lib/request/adapters/http.ts +++ b/src/lib/request/adapters/http.ts @@ -231,12 +231,12 @@ export class HttpAdapter implements AdapterInterface { return reject(new FsRequestError(err.message, config, null, FsRequestErrorCode.NETWORK)); }); - stream.on('end', () => { + stream.on('end', async () => { // check if there is any response data inside if (res.statusCode !== 204) { // prepare response response.data = Buffer.concat(responseBuffer); - response = parseResponse(response); + response = await parseResponse(response); } else { response.data = null; } diff --git a/src/lib/request/adapters/xhr.ts b/src/lib/request/adapters/xhr.ts index 029e9d4d..fd7a31f2 100644 --- a/src/lib/request/adapters/xhr.ts +++ b/src/lib/request/adapters/xhr.ts @@ -69,7 +69,7 @@ export class XhrAdapter implements AdapterInterface { request.timeout = config.timeout; return new Promise((resolve, reject) => { - request.onreadystatechange = () => { + request.onreadystatechange = async () => { if (!request || request.readyState !== 4) { return; } @@ -91,7 +91,7 @@ export class XhrAdapter implements AdapterInterface { }; request = null; - response = parseResponse(response); + response = await parseResponse(response); if (500 <= response.status && response.status <= 599) { // server error throw diff --git a/src/lib/request/helpers/data.spec.browser.ts b/src/lib/request/helpers/data.spec.ts similarity index 58% rename from src/lib/request/helpers/data.spec.browser.ts rename to src/lib/request/helpers/data.spec.ts index e93f4f78..44c24256 100644 --- a/src/lib/request/helpers/data.spec.browser.ts +++ b/src/lib/request/helpers/data.spec.ts @@ -36,7 +36,7 @@ describe('Request/Helpers/Data', () => { }); describe('parse response', () => { - it('should return equal response data', () => { + it('should return equal response data', async () => { const response = { status: 200, statusText: 'ok', @@ -46,10 +46,10 @@ describe('Request/Helpers/Data', () => { url: 'https://filestack.com', }, }; - expect(parseResponse(response)).toEqual(response); + expect(await parseResponse(response)).toEqual(response); }); - it('should return response with application/json and data stringify', () => { + it('should return response with application/json and data stringify', async () => { const response = { status: 200, statusText: 'ok', @@ -61,10 +61,10 @@ describe('Request/Helpers/Data', () => { url: 'https://filestack.com', }, }; - expect(parseResponse(response)).toEqual(response); + expect(await parseResponse(response)).toEqual(response); }); - it('should return response with application/json and json data ', () => { + it('should return response with application/json and json data ', async () => { const response = { status: 200, statusText: 'ok', @@ -76,10 +76,10 @@ describe('Request/Helpers/Data', () => { url: 'https://filestack.com', }, }; - expect(parseResponse(response)).toEqual(response); + expect(await parseResponse(response)).toEqual(response); }); - it('should return text/plain response with ArrayBuffer ', () => { + it('should return text/plain response with ArrayBuffer ', async () => { const response = { status: 200, statusText: 'ok', @@ -91,7 +91,35 @@ describe('Request/Helpers/Data', () => { url: 'https://filestack.com', }, }; - expect(parseResponse(response)).toEqual(response); + expect(await parseResponse(response)).toEqual(response); + }); + + it('should parse application/xml response to json', async () => { + const response = { + status: 200, + statusText: 'ok', + headers: { + 'content-type': 'application/xml', + }, + data: Buffer.from('RequestTimeTooSkewedThe difference between the request time and the current time is toolarge.20191102T153031Z2019-11-02T15:56:35Z9000006C8855BC97D17A1B3bwhtSpY9tAypFr9L6V+W6UAxFUyk7mK+VQGhIu4Bxj0t7jhQWMEEinW4YHpi8Q9qONnx1CEHKE='), + config: { + url: 'https://filestack.com', + }, + }; + + const res = await parseResponse(response); + + return expect(res.data).toEqual({ + Error: { + code: 'RequestTimeTooSkewed', + Message: 'The difference between the request time and the current time is toolarge.', + RequestTime: '20191102T153031Z', + ServerTime: '2019-11-02T15:56:35Z', + MaxAllowedSkewMilliseconds: 900000, + RequestId: '6C8855BC97D17A1B', + HostId: '3bwhtSpY9tAypFr9L6V+W6UAxFUyk7mK+VQGhIu4Bxj0t7jhQWMEEinW4YHpi8Q9qONnx1CEHKE=', + }, + }); }); }); }); diff --git a/src/lib/request/helpers/data.ts b/src/lib/request/helpers/data.ts index b3e080db..6315ecd5 100644 --- a/src/lib/request/helpers/data.ts +++ b/src/lib/request/helpers/data.ts @@ -18,6 +18,7 @@ import { isURLSearchParams, isObject, isStream, isFormData, isArrayBuffer, isFil import { getVersion, uniqueId } from './../../utils'; import { FsRequestOptions, FsResponse } from './../types'; import { set } from './headers'; +import * as parser from 'fast-xml-parser'; import Debug from 'debug'; const debug = Debug('fs:request:data'); @@ -74,9 +75,9 @@ export const filestackHeaders = (config: FsRequestOptions) => { * * @param response */ -export const parseResponse = (response: FsResponse): FsResponse => { +export const parseResponse = async (response: FsResponse): Promise => { if (!response.headers || !response.headers['content-type']) { - return response; + return Promise.resolve(response); } const contentType = response.headers['content-type']; @@ -91,10 +92,22 @@ export const parseResponse = (response: FsResponse): FsResponse => { if (isBuffer(response.data)) { response.data = bufferToString(response.data); } - // if its not a buffer its probably plain text + } else if (/application\/xml/.test(contentType)) { + let data = response.data; + + if (isBuffer(response.data)) { + data = bufferToString(response.data); + } + + if (parser.validate(data) === true) { + response.data = parser.parse(data, { + ignoreAttributes : true, + trimValues: true, + }); + } } - return response; + return Promise.resolve(response); }; function bufferToString(buffer) { diff --git a/src/lib/request/utils.node.ts b/src/lib/request/utils.node.ts index cf9e91f4..9b504e2c 100644 --- a/src/lib/request/utils.node.ts +++ b/src/lib/request/utils.node.ts @@ -15,6 +15,7 @@ * limitations under the License. */ import * as stream from 'stream'; + import { isObject } from './utils'; /** diff --git a/src/lib/utils/extensions.ts b/src/lib/utils/extensions.ts index 5ec33140..4d8c39fa 100644 --- a/src/lib/utils/extensions.ts +++ b/src/lib/utils/extensions.ts @@ -95,10 +95,7 @@ export const Map = { 'deb', 'dmg', 'iso', - 'img', - 'msi', 'msp', - 'msm', 'buffer', ], 'application/oda': ['oda'], @@ -259,6 +256,9 @@ export const Map = { 'text/n3': ['n3'], 'text/plain': ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'ini'], 'text/richtext': ['rtx'], + 'application/font-ttf': 'ttf', + 'application/vnd.ms-fontobject': 'eot', + 'application/font-otf': 'otf', 'text/rtf': ['*rtf'], 'text/sgml': ['sgml', 'sgm'], 'text/shex': ['shex'], @@ -268,7 +268,7 @@ export const Map = { 'text/troff': ['t', 'tr', 'roff', 'man', 'me', 'ms'], 'text/turtle': ['ttl'], 'text/uri-list': ['uri', 'uris', 'urls'], - 'text/vcard': ['vcard'], + 'text/vcard': ['vcard', 'vcr'], 'text/vtt': ['vtt'], 'text/xml': ['*xml'], 'text/yaml': ['yaml', 'yml'], @@ -491,7 +491,6 @@ export const Map = { 'application/vnd.ms-excel.sheet.binary.macroenabled.12': ['xlsb'], 'application/vnd.ms-excel.sheet.macroenabled.12': ['xlsm'], 'application/vnd.ms-excel.template.macroenabled.12': ['xltm'], - 'application/vnd.ms-fontobject': ['eot'], 'application/vnd.ms-htmlhelp': ['chm'], 'application/vnd.ms-ims': ['ims'], 'application/vnd.ms-lrm': ['lrm'], @@ -924,4 +923,21 @@ export const Map = { 'video/x-sgi-movie': ['movie'], 'video/x-smv': ['smv'], 'x-conference/x-cooltalk': ['ice'], + 'application/x-msi': 'msi', + 'application/x-ms': 'ms', + 'application/vnd.geo+json': 'geojson', + 'application/vnd.debian.binary-package': 'deb', + 'application/font-woff': 'woff', + 'application/font-woff2': 'woff2', + 'application/x-font-ttf': ['ttc', 'ttf'], + 'font/opentype': 'otf', + 'application/x-bb-appworld': 'bbaw', + 'application/x-cd-image': 'iso', + 'application/x-opera-extension': 'oex', + 'application/x-ms-dos-executable': 'exe', + 'text/vnd.rim.location.xloc': 'xloc', + 'application/x-desktop': 'desktop', + 'application/x-typescript': 'ts', + 'application/x-java-archive': 'jar', + 'application/x-sharedlib': 'so', }; diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 4ba76261..70ab5c0c 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -21,6 +21,86 @@ import { Map } from './extensions'; import fileType from 'file-type'; import * as isutf8 from 'isutf8'; +// more info https://gist.github.com/AshHeskes/6038140 +const extensionToMime = { + 'text/vnd.dvb.subtitle': 'sub', + 'application/x-msmetafile': 'wmz', + 'application/x-msi': 'msi', + 'application/x-ms': 'ms', + 'application/atom+xml': 'atom', + 'application/json': ['json', 'map', 'topojson'], + 'application/ld+json': 'jsonld', + 'application/rss+xml': 'rss', + 'application/vnd.geo+json': 'geojson', + 'application/xml': ['rdf', 'xml'], + 'application/javascript': 'js', + 'application/manifest+json': 'webmanifest', + 'application/x-web-app-manifest+json': 'webapp', + 'text/cache-manifest': 'appcache', + 'image/x-icon': ['cur', 'ico'], + 'application/msword': 'doc', + 'application/vnd.ms-excel': 'xls', + 'application/vnd.ms-powerpoint': 'ppt', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx', + 'application/vnd.debian.binary-package': 'deb', + 'application/font-woff': 'woff', + 'application/font-woff2': 'woff2', + 'application/vnd.ms-fontobject': 'eot', + 'application/x-font-ttf': ['ttc', 'ttf'], + 'font/opentype': 'otf', + 'application/java-archive': ['ear', 'jar', 'war'], + 'application/mac-binhex40': 'hqx', + 'application/octet-stream': ['bin', 'deb', 'dll', 'dmg', 'img', 'iso', 'msi', 'msm', 'msp', 'safariextz'], + 'application/postscript': ['ai', 'eps', 'ps'], + 'application/rtf': 'rtf', + 'application/vnd.google-earth.kml+xml': 'kml', + 'application/vnd.google-earth.kmz': 'kmz', + 'application/vnd.wap.wmlc': 'wmlc', + 'application/x-7z-compressed': '7z', + 'application/x-bb-appworld': 'bbaw', + 'application/x-bittorrent': 'torrent', + 'application/x-chrome-extension': 'crx', + 'application/x-cocoa': 'cco', + 'application/x-java-archive-diff': 'jardiff', + 'application/x-java-jnlp-file': 'jnlp', + 'application/x-makeself': 'run', + 'application/x-cd-image': 'iso', + 'application/x-opera-extension': 'oex', + 'application/x-perl': ['pl', 'pm'], + 'application/x-pilot': ['pdb', 'prc'], + 'application/x-rar-compressed': 'rar', + 'application/x-redhat-package-manager': 'rpm', + 'application/x-sea': 'sea', + 'application/x-shockwave-flash': 'swf', + 'application/x-stuffit': 'sit', + 'application/x-tcl': ['tcl', 'tk'], + 'application/x-x509-ca-cert': ['crt', 'der', 'pem'], + 'application/x-xpinstall': 'xpi', + 'application/x-ms-dos-executable': 'exe', + 'application/xhtml+xml': 'xhtml', + 'application/xslt+xml': 'xsl', + 'application/zip': 'zip', + 'text/css': 'css', + 'text/csv': 'csv', + 'text/html': ['htm', 'html', 'shtml'], + 'text/markdown': 'md', + 'text/mathml': 'mml', + 'text/plain': 'txt', + 'text/vcard': ['vcard', 'vcf'], + 'text/vnd.rim.location.xloc': 'xloc', + 'text/vnd.sun.j2me.app-descriptor': 'jad', + 'text/vnd.wap.wml': 'wml', + 'text/vtt': 'vtt', + 'text/x-component': 'htc', + 'application/x-desktop': 'desktop', + 'text/x-markdown': 'md', + 'application/x-typescript': 'ts', + 'application/x-java-archive': 'jar', + 'application/x-sharedlib': 'so', +}; + /** * Resolve cdn url based on handle type * @@ -104,8 +184,7 @@ export const uniqueId = (len: number = 10): string => { export const getMimetype = (file: Uint8Array | Buffer, name?: string): string => { let type = fileType(file); - // check x-ms and x-msi by extension - if (type && type.mime !== 'application/x-ms' && type.mime !== 'application/x-msi') { + if (type && ['text/plain', 'application/octet-stream', 'application/x-ms', 'application/x-msi'].indexOf(type.mime) === -1) { return type.mime; } @@ -209,7 +288,7 @@ export const cleanUpCallbacks = (obj: any) => { return obj; } - Object.keys(obj).forEach((k) => { + Object.keys(obj).forEach(k => { if (typeof obj[k] === 'function') { obj[k] = undefined; }