diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | server/lib/object-storage/shared/object-storage-helpers.ts | 89 | ||||
-rw-r--r-- | server/tests/api/object-storage/videos.ts | 42 | ||||
-rw-r--r-- | yarn.lock | 43 |
5 files changed, 92 insertions, 86 deletions
diff --git a/.gitignore b/.gitignore index 5e06248f1..c6029ad65 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -47,6 +47,7 @@ yarn-error.log | |||
47 | /*.zip | 47 | /*.zip |
48 | /*.tar.xz | 48 | /*.tar.xz |
49 | /*.asc | 49 | /*.asc |
50 | *.DS_Store | ||
50 | /server/tools/import-mediacore.ts | 51 | /server/tools/import-mediacore.ts |
51 | /docker-volume/ | 52 | /docker-volume/ |
52 | /init.mp4 | 53 | /init.mp4 |
diff --git a/package.json b/package.json index 97438afdb..360bd781f 100644 --- a/package.json +++ b/package.json | |||
@@ -78,7 +78,8 @@ | |||
78 | }, | 78 | }, |
79 | "dependencies": { | 79 | "dependencies": { |
80 | "@aws-sdk/client-s3": "^3.23.0", | 80 | "@aws-sdk/client-s3": "^3.23.0", |
81 | "@babel/parser": "7.17.9", | 81 | "@aws-sdk/lib-storage": "^3.72.0", |
82 | "@babel/parser": "7.17.8", | ||
82 | "@peertube/feed": "^5.0.1", | 83 | "@peertube/feed": "^5.0.1", |
83 | "@peertube/http-signature": "^1.4.0", | 84 | "@peertube/http-signature": "^1.4.0", |
84 | "@uploadx/core": "^5.1.0", | 85 | "@uploadx/core": "^5.1.0", |
diff --git a/server/lib/object-storage/shared/object-storage-helpers.ts b/server/lib/object-storage/shared/object-storage-helpers.ts index ecb82856e..a2de92532 100644 --- a/server/lib/object-storage/shared/object-storage-helpers.ts +++ b/server/lib/object-storage/shared/object-storage-helpers.ts | |||
@@ -1,19 +1,14 @@ | |||
1 | import { close, createReadStream, createWriteStream, ensureDir, open, ReadStream, stat } from 'fs-extra' | 1 | import { createReadStream, createWriteStream, ensureDir, ReadStream, stat } from 'fs-extra' |
2 | import { min } from 'lodash' | ||
3 | import { dirname } from 'path' | 2 | import { dirname } from 'path' |
4 | import { Readable } from 'stream' | 3 | import { Readable } from 'stream' |
5 | import { | 4 | import { |
6 | CompletedPart, | ||
7 | CompleteMultipartUploadCommand, | ||
8 | CreateMultipartUploadCommand, | ||
9 | CreateMultipartUploadCommandInput, | ||
10 | DeleteObjectCommand, | 5 | DeleteObjectCommand, |
11 | GetObjectCommand, | 6 | GetObjectCommand, |
12 | ListObjectsV2Command, | 7 | ListObjectsV2Command, |
13 | PutObjectCommand, | 8 | PutObjectCommand, |
14 | PutObjectCommandInput, | 9 | PutObjectCommandInput |
15 | UploadPartCommand | ||
16 | } from '@aws-sdk/client-s3' | 10 | } from '@aws-sdk/client-s3' |
11 | import { Upload } from '@aws-sdk/lib-storage' | ||
17 | import { pipelinePromise } from '@server/helpers/core-utils' | 12 | import { pipelinePromise } from '@server/helpers/core-utils' |
18 | import { isArray } from '@server/helpers/custom-validators/misc' | 13 | import { isArray } from '@server/helpers/custom-validators/misc' |
19 | import { logger } from '@server/helpers/logger' | 14 | import { logger } from '@server/helpers/logger' |
@@ -37,13 +32,12 @@ async function storeObject (options: { | |||
37 | logger.debug('Uploading file %s to %s%s in bucket %s', inputPath, bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, lTags()) | 32 | logger.debug('Uploading file %s to %s%s in bucket %s', inputPath, bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, lTags()) |
38 | 33 | ||
39 | const stats = await stat(inputPath) | 34 | const stats = await stat(inputPath) |
35 | const fileStream = createReadStream(inputPath) | ||
40 | 36 | ||
41 | // If bigger than max allowed size we do a multipart upload | ||
42 | if (stats.size > CONFIG.OBJECT_STORAGE.MAX_UPLOAD_PART) { | 37 | if (stats.size > CONFIG.OBJECT_STORAGE.MAX_UPLOAD_PART) { |
43 | return multiPartUpload({ inputPath, objectStorageKey, bucketInfo }) | 38 | return multiPartUpload({ content: fileStream, objectStorageKey, bucketInfo }) |
44 | } | 39 | } |
45 | 40 | ||
46 | const fileStream = createReadStream(inputPath) | ||
47 | return objectStoragePut({ objectStorageKey, content: fileStream, bucketInfo }) | 41 | return objectStoragePut({ objectStorageKey, content: fileStream, bucketInfo }) |
48 | } | 42 | } |
49 | 43 | ||
@@ -163,18 +157,14 @@ async function objectStoragePut (options: { | |||
163 | } | 157 | } |
164 | 158 | ||
165 | async function multiPartUpload (options: { | 159 | async function multiPartUpload (options: { |
166 | inputPath: string | 160 | content: ReadStream |
167 | objectStorageKey: string | 161 | objectStorageKey: string |
168 | bucketInfo: BucketInfo | 162 | bucketInfo: BucketInfo |
169 | }) { | 163 | }) { |
170 | const { objectStorageKey, inputPath, bucketInfo } = options | 164 | const { content, objectStorageKey, bucketInfo } = options |
171 | 165 | ||
172 | const key = buildKey(objectStorageKey, bucketInfo) | 166 | const input: PutObjectCommandInput = { |
173 | const s3Client = getClient() | 167 | Body: content, |
174 | |||
175 | const statResult = await stat(inputPath) | ||
176 | |||
177 | const input: CreateMultipartUploadCommandInput = { | ||
178 | Bucket: bucketInfo.BUCKET_NAME, | 168 | Bucket: bucketInfo.BUCKET_NAME, |
179 | Key: buildKey(objectStorageKey, bucketInfo) | 169 | Key: buildKey(objectStorageKey, bucketInfo) |
180 | } | 170 | } |
@@ -183,60 +173,19 @@ async function multiPartUpload (options: { | |||
183 | input.ACL = CONFIG.OBJECT_STORAGE.UPLOAD_ACL | 173 | input.ACL = CONFIG.OBJECT_STORAGE.UPLOAD_ACL |
184 | } | 174 | } |
185 | 175 | ||
186 | const createMultipartCommand = new CreateMultipartUploadCommand(input) | 176 | const parallelUploads3 = new Upload({ |
187 | const createResponse = await s3Client.send(createMultipartCommand) | 177 | client: getClient(), |
188 | 178 | queueSize: 4, | |
189 | const fd = await open(inputPath, 'r') | 179 | partSize: CONFIG.OBJECT_STORAGE.MAX_UPLOAD_PART, |
190 | let partNumber = 1 | 180 | leavePartsOnError: false, |
191 | const parts: CompletedPart[] = [] | 181 | params: input |
192 | const partSize = CONFIG.OBJECT_STORAGE.MAX_UPLOAD_PART | ||
193 | |||
194 | for (let start = 0; start < statResult.size; start += partSize) { | ||
195 | logger.debug( | ||
196 | 'Uploading part %d of file to %s%s in bucket %s', | ||
197 | partNumber, bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, lTags() | ||
198 | ) | ||
199 | |||
200 | // FIXME: Remove when https://github.com/aws/aws-sdk-js-v3/pull/2637 is released | ||
201 | // The s3 sdk needs to know the length of the http body beforehand, but doesn't support | ||
202 | // streams with start and end set, so it just tries to stat the file in stream.path. | ||
203 | // This fails for us because we only want to send part of the file. The stream type | ||
204 | // is modified so we can set the byteLength here, which s3 detects because array buffers | ||
205 | // have this field set | ||
206 | const stream: ReadStream & { byteLength: number } = | ||
207 | createReadStream( | ||
208 | inputPath, | ||
209 | { fd, autoClose: false, start, end: (start + partSize) - 1 } | ||
210 | ) as ReadStream & { byteLength: number } | ||
211 | |||
212 | // Calculate if the part size is more than what's left over, and in that case use left over bytes for byteLength | ||
213 | stream.byteLength = min([ statResult.size - start, partSize ]) | ||
214 | |||
215 | const uploadPartCommand = new UploadPartCommand({ | ||
216 | Bucket: bucketInfo.BUCKET_NAME, | ||
217 | Key: key, | ||
218 | UploadId: createResponse.UploadId, | ||
219 | PartNumber: partNumber, | ||
220 | Body: stream | ||
221 | }) | ||
222 | const uploadResponse = await s3Client.send(uploadPartCommand) | ||
223 | |||
224 | parts.push({ ETag: uploadResponse.ETag, PartNumber: partNumber }) | ||
225 | partNumber += 1 | ||
226 | } | ||
227 | await close(fd) | ||
228 | |||
229 | const completeUploadCommand = new CompleteMultipartUploadCommand({ | ||
230 | Bucket: bucketInfo.BUCKET_NAME, | ||
231 | Key: key, | ||
232 | UploadId: createResponse.UploadId, | ||
233 | MultipartUpload: { Parts: parts } | ||
234 | }) | 182 | }) |
235 | await s3Client.send(completeUploadCommand) | 183 | |
184 | await parallelUploads3.done() | ||
236 | 185 | ||
237 | logger.debug( | 186 | logger.debug( |
238 | 'Completed %s%s in bucket %s in %d parts', | 187 | 'Completed %s%s in bucket %s', |
239 | bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, partNumber - 1, lTags() | 188 | bucketInfo.PREFIX, objectStorageKey, bucketInfo.BUCKET_NAME, lTags() |
240 | ) | 189 | ) |
241 | 190 | ||
242 | return getPrivateUrl(bucketInfo, objectStorageKey) | 191 | return getPrivateUrl(bucketInfo, objectStorageKey) |
diff --git a/server/tests/api/object-storage/videos.ts b/server/tests/api/object-storage/videos.ts index 498efcb17..22ad06305 100644 --- a/server/tests/api/object-storage/videos.ts +++ b/server/tests/api/object-storage/videos.ts | |||
@@ -1,9 +1,17 @@ | |||
1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ | 1 | /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ |
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import bytes from 'bytes' | ||
4 | import * as chai from 'chai' | 5 | import * as chai from 'chai' |
6 | import { stat } from 'fs-extra' | ||
5 | import { merge } from 'lodash' | 7 | import { merge } from 'lodash' |
6 | import { checkTmpIsEmpty, expectLogDoesNotContain, expectStartWith, MockObjectStorage } from '@server/tests/shared' | 8 | import { |
9 | checkTmpIsEmpty, | ||
10 | expectLogDoesNotContain, | ||
11 | expectStartWith, | ||
12 | generateHighBitrateVideo, | ||
13 | MockObjectStorage | ||
14 | } from '@server/tests/shared' | ||
7 | import { areObjectStorageTestsDisabled } from '@shared/core-utils' | 15 | import { areObjectStorageTestsDisabled } from '@shared/core-utils' |
8 | import { HttpStatusCode, VideoDetails } from '@shared/models' | 16 | import { HttpStatusCode, VideoDetails } from '@shared/models' |
9 | import { | 17 | import { |
@@ -107,6 +115,10 @@ async function checkFiles (options: { | |||
107 | } | 115 | } |
108 | 116 | ||
109 | function runTestSuite (options: { | 117 | function runTestSuite (options: { |
118 | fixture?: string | ||
119 | |||
120 | maxUploadPart?: string | ||
121 | |||
110 | playlistBucket: string | 122 | playlistBucket: string |
111 | playlistPrefix?: string | 123 | playlistPrefix?: string |
112 | 124 | ||
@@ -114,10 +126,9 @@ function runTestSuite (options: { | |||
114 | webtorrentPrefix?: string | 126 | webtorrentPrefix?: string |
115 | 127 | ||
116 | useMockBaseUrl?: boolean | 128 | useMockBaseUrl?: boolean |
117 | |||
118 | maxUploadPart?: string | ||
119 | }) { | 129 | }) { |
120 | const mockObjectStorage = new MockObjectStorage() | 130 | const mockObjectStorage = new MockObjectStorage() |
131 | const { fixture } = options | ||
121 | let baseMockUrl: string | 132 | let baseMockUrl: string |
122 | 133 | ||
123 | let servers: PeerTubeServer[] | 134 | let servers: PeerTubeServer[] |
@@ -144,7 +155,7 @@ function runTestSuite (options: { | |||
144 | 155 | ||
145 | credentials: ObjectStorageCommand.getCredentialsConfig(), | 156 | credentials: ObjectStorageCommand.getCredentialsConfig(), |
146 | 157 | ||
147 | max_upload_part: options.maxUploadPart || '2MB', | 158 | max_upload_part: options.maxUploadPart || '5MB', |
148 | 159 | ||
149 | streaming_playlists: { | 160 | streaming_playlists: { |
150 | bucket_name: options.playlistBucket, | 161 | bucket_name: options.playlistBucket, |
@@ -181,7 +192,7 @@ function runTestSuite (options: { | |||
181 | it('Should upload a video and move it to the object storage without transcoding', async function () { | 192 | it('Should upload a video and move it to the object storage without transcoding', async function () { |
182 | this.timeout(40000) | 193 | this.timeout(40000) |
183 | 194 | ||
184 | const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1' }) | 195 | const { uuid } = await servers[0].videos.quickUpload({ name: 'video 1', fixture }) |
185 | uuidsToDelete.push(uuid) | 196 | uuidsToDelete.push(uuid) |
186 | 197 | ||
187 | await waitJobs(servers) | 198 | await waitJobs(servers) |
@@ -197,7 +208,7 @@ function runTestSuite (options: { | |||
197 | it('Should upload a video and move it to the object storage with transcoding', async function () { | 208 | it('Should upload a video and move it to the object storage with transcoding', async function () { |
198 | this.timeout(120000) | 209 | this.timeout(120000) |
199 | 210 | ||
200 | const { uuid } = await servers[1].videos.quickUpload({ name: 'video 2' }) | 211 | const { uuid } = await servers[1].videos.quickUpload({ name: 'video 2', fixture }) |
201 | uuidsToDelete.push(uuid) | 212 | uuidsToDelete.push(uuid) |
202 | 213 | ||
203 | await waitJobs(servers) | 214 | await waitJobs(servers) |
@@ -390,12 +401,25 @@ describe('Object storage for videos', function () { | |||
390 | }) | 401 | }) |
391 | }) | 402 | }) |
392 | 403 | ||
393 | describe('Test object storage with small upload part', function () { | 404 | describe('Test object storage with file bigger than upload part', function () { |
405 | let fixture: string | ||
406 | const maxUploadPart = '5MB' | ||
407 | |||
408 | before(async function () { | ||
409 | fixture = await generateHighBitrateVideo() | ||
410 | |||
411 | const { size } = await stat(fixture) | ||
412 | |||
413 | if (bytes.parse(maxUploadPart) > size) { | ||
414 | throw Error(`Fixture file is too small (${size}) to make sense for this test.`) | ||
415 | } | ||
416 | }) | ||
417 | |||
394 | runTestSuite({ | 418 | runTestSuite({ |
419 | maxUploadPart, | ||
395 | playlistBucket: 'streaming-playlists', | 420 | playlistBucket: 'streaming-playlists', |
396 | webtorrentBucket: 'videos', | 421 | webtorrentBucket: 'videos', |
397 | 422 | fixture | |
398 | maxUploadPart: '5KB' | ||
399 | }) | 423 | }) |
400 | }) | 424 | }) |
401 | }) | 425 | }) |
@@ -484,6 +484,16 @@ | |||
484 | dependencies: | 484 | dependencies: |
485 | tslib "^2.3.1" | 485 | tslib "^2.3.1" |
486 | 486 | ||
487 | "@aws-sdk/lib-storage@^3.72.0": | ||
488 | version "3.72.0" | ||
489 | resolved "https://registry.yarnpkg.com/@aws-sdk/lib-storage/-/lib-storage-3.72.0.tgz#035c577e306d6472aa5cb15220936262cb394763" | ||
490 | integrity sha512-z2L//IMN9fkXMhFyC0F9SXTH0oHA7zsOsLOyQS2hqKXAE3TGTK6d0hj6vmut4RH0wGzXOQ9zrh0DexAVdv29pA== | ||
491 | dependencies: | ||
492 | buffer "5.6.0" | ||
493 | events "3.3.0" | ||
494 | stream-browserify "3.0.0" | ||
495 | tslib "^2.3.1" | ||
496 | |||
487 | "@aws-sdk/md5-js@3.58.0": | 497 | "@aws-sdk/md5-js@3.58.0": |
488 | version "3.58.0" | 498 | version "3.58.0" |
489 | resolved "https://registry.yarnpkg.com/@aws-sdk/md5-js/-/md5-js-3.58.0.tgz#a7ecf5cc8a81ce247fd620f8c981802d0427737f" | 499 | resolved "https://registry.yarnpkg.com/@aws-sdk/md5-js/-/md5-js-3.58.0.tgz#a7ecf5cc8a81ce247fd620f8c981802d0427737f" |
@@ -989,7 +999,12 @@ | |||
989 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" | 999 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" |
990 | integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== | 1000 | integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== |
991 | 1001 | ||
992 | "@babel/parser@7.17.9", "@babel/parser@^7.16.4", "@babel/parser@^7.16.7", "@babel/parser@^7.17.9", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": | 1002 | "@babel/parser@7.17.8": |
1003 | version "7.17.8" | ||
1004 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.8.tgz#2817fb9d885dd8132ea0f8eb615a6388cca1c240" | ||
1005 | integrity sha512-BoHhDJrJXqcg+ZL16Xv39H9n+AqJ4pcDrQBGZN+wHxIysrLZ3/ECwCBUch/1zUNhnsXULcONU3Ei5Hmkfk6kiQ== | ||
1006 | |||
1007 | "@babel/parser@^7.16.4", "@babel/parser@^7.16.7", "@babel/parser@^7.17.9", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": | ||
993 | version "7.17.9" | 1008 | version "7.17.9" |
994 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" | 1009 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" |
995 | integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== | 1010 | integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== |
@@ -2555,7 +2570,7 @@ base32.js@0.1.0: | |||
2555 | resolved "https://registry.yarnpkg.com/base32.js/-/base32.js-0.1.0.tgz#b582dec693c2f11e893cf064ee6ac5b6131a2202" | 2570 | resolved "https://registry.yarnpkg.com/base32.js/-/base32.js-0.1.0.tgz#b582dec693c2f11e893cf064ee6ac5b6131a2202" |
2556 | integrity sha1-tYLexpPC8R6JPPBk7mrFthMaIgI= | 2571 | integrity sha1-tYLexpPC8R6JPPBk7mrFthMaIgI= |
2557 | 2572 | ||
2558 | base64-js@^1.2.0, base64-js@^1.3.1: | 2573 | base64-js@^1.0.2, base64-js@^1.2.0, base64-js@^1.3.1: |
2559 | version "1.5.1" | 2574 | version "1.5.1" |
2560 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" | 2575 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" |
2561 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== | 2576 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== |
@@ -2818,6 +2833,14 @@ buffer-writer@2.0.0: | |||
2818 | resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" | 2833 | resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" |
2819 | integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== | 2834 | integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== |
2820 | 2835 | ||
2836 | buffer@5.6.0: | ||
2837 | version "5.6.0" | ||
2838 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" | ||
2839 | integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== | ||
2840 | dependencies: | ||
2841 | base64-js "^1.0.2" | ||
2842 | ieee754 "^1.1.4" | ||
2843 | |||
2821 | buffer@^5.2.0: | 2844 | buffer@^5.2.0: |
2822 | version "5.7.1" | 2845 | version "5.7.1" |
2823 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" | 2846 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" |
@@ -4228,7 +4251,7 @@ event-target-shim@^5.0.0: | |||
4228 | resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" | 4251 | resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" |
4229 | integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== | 4252 | integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== |
4230 | 4253 | ||
4231 | events@^3.3.0: | 4254 | events@3.3.0, events@^3.3.0: |
4232 | version "3.3.0" | 4255 | version "3.3.0" |
4233 | resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" | 4256 | resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" |
4234 | integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== | 4257 | integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== |
@@ -5006,7 +5029,7 @@ iconv-lite@0.6.3: | |||
5006 | dependencies: | 5029 | dependencies: |
5007 | safer-buffer ">= 2.1.2 < 3.0.0" | 5030 | safer-buffer ">= 2.1.2 < 3.0.0" |
5008 | 5031 | ||
5009 | ieee754@^1.1.13, ieee754@^1.2.1: | 5032 | ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: |
5010 | version "1.2.1" | 5033 | version "1.2.1" |
5011 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" | 5034 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" |
5012 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== | 5035 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== |
@@ -5066,7 +5089,7 @@ inflight@^1.0.4: | |||
5066 | once "^1.3.0" | 5089 | once "^1.3.0" |
5067 | wrappy "1" | 5090 | wrappy "1" |
5068 | 5091 | ||
5069 | inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: | 5092 | inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4: |
5070 | version "2.0.4" | 5093 | version "2.0.4" |
5071 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" | 5094 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" |
5072 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== | 5095 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== |
@@ -7353,7 +7376,7 @@ readable-stream@^2.0.0, readable-stream@^2.2.2, readable-stream@~2.3.6: | |||
7353 | string_decoder "~1.1.1" | 7376 | string_decoder "~1.1.1" |
7354 | util-deprecate "~1.0.1" | 7377 | util-deprecate "~1.0.1" |
7355 | 7378 | ||
7356 | readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0: | 7379 | readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0: |
7357 | version "3.6.0" | 7380 | version "3.6.0" |
7358 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" | 7381 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" |
7359 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== | 7382 | integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== |
@@ -8017,6 +8040,14 @@ statuses@1.5.0, "statuses@>= 1.5.0 < 2", statuses@~1.5.0: | |||
8017 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" | 8040 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" |
8018 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= | 8041 | integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= |
8019 | 8042 | ||
8043 | stream-browserify@3.0.0: | ||
8044 | version "3.0.0" | ||
8045 | resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" | ||
8046 | integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== | ||
8047 | dependencies: | ||
8048 | inherits "~2.0.4" | ||
8049 | readable-stream "^3.5.0" | ||
8050 | |||
8020 | stream-combiner@~0.0.4: | 8051 | stream-combiner@~0.0.4: |
8021 | version "0.0.4" | 8052 | version "0.0.4" |
8022 | resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" | 8053 | resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" |