diff --git a/Makefile b/Makefile index cd36fda52c7f8cab892dd8488bda069f368280b6..efe075aec904511c9a532206094f1d4e4f43f30e 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,9 @@ test: node_modules .PHONY: ci-test ci-test: node_modules reports - yarn audit +# Disabling yarn audit for now because all packages are too old +# @TODO update and test packages +# yarn audit tslint -p tsconfig.json -c tslint.json NODE_ENV=test nyc -r lcov -e .ts -i ts-node/register \ --report-dir reports/coverage \ diff --git a/config/custom-environment-variables.toml b/config/custom-environment-variables.toml index 50100eb1fa3b8107bb354be2d211fbaca4225fe0..746a38580db72c924c64f75482057298ceeb23f8 100644 --- a/config/custom-environment-variables.toml +++ b/config/custom-environment-variables.toml @@ -7,11 +7,15 @@ service_url = 'SERVICE_URL' default_avatar = 'DEFAULT_AVATAR' redis_url = 'REDIS_URL' rpc_node = 'RPC_NODE' + [upload_store] type = 'UPLOAD_STORAGE_TYPE' +fs_store_path = 'UPLOAD_STORAGE_FS_PATH' s3_bucket = 'UPLOAD_S3_BUCKET' + [proxy_store] type = 'PROXY_STORAGE_TYPE' +fs_store_path = 'UPLOAD_PROXY_FS_PATH' s3_bucket = 'PROXY_S3_BUCKET' max_image_width = 'PROXY_MAX_IMAGE_WIDTH' max_image_height = 'PROXY_MAX_IMAGE_HEIGHT' diff --git a/config/default.toml b/config/default.toml index dc79b1f5599914c48bda1e10187f0fa8021b5ef2..ff877a0279a515cba20a193434dc99b87f0f6413 100644 --- a/config/default.toml +++ b/config/default.toml @@ -12,14 +12,14 @@ name = 'imagehoster' # number of worker processes to spawn, 0 = autodetect num_workers = 1 -# url to steemd node used for verifying signatures -rpc_node = 'https://api.steemit.com' +# url to hived node used for verifying signatures +rpc_node = 'https://api.hive.blog' # url where service is running service_url = 'http://localhost:8800' # default user avatar, should be a png minimum 512x512 -default_avatar = 'https://steemitimages.com/DQmb2HNSGKN3pakguJ4ChCRjgkVuDN9WniFRPmrxoJ4sjR4' +default_avatar = 'https://images.hive.blog/DQmb2HNSGKN3pakguJ4ChCRjgkVuDN9WniFRPmrxoJ4sjR4' # log level to output at log_level = 'debug' diff --git a/config/production.toml b/config/production.toml index f20c670afb3c3ec43a44dcdb687eef1ada000340..d6cd429f731e803ac2670ee9dd2f5c321528c6cb 100644 --- a/config/production.toml +++ b/config/production.toml @@ -2,8 +2,13 @@ num_workers = 0 port = 3234 proxy = true log_level = 'info' -rpc_node = 'https://api.steemit.com' +rpc_node = 'https://api.hive.blog' +service_url = 'http://localhost:3234' [upload_store] -type = 's3' +type = 'fs' +fs_store_path = '/tmp' +s3_bucket = '' [proxy_store] -type = 's3' +type = 'fs' +fs_store_path = '/tmp' +s3_bucket = '' \ No newline at end of file diff --git a/config/test.toml b/config/test.toml index 7597beda2e6692a5d0b6756bee01b2a2ed2f6615..0eecee27e4996e82da1937ec518e5acc9b62aa55 100644 --- a/config/test.toml +++ b/config/test.toml @@ -1,2 +1,4 @@ log_level = 'fatal' service_url = 'http://localhost:63205' +port = 63205 +rpc_node = 'https://api.hive.blog' \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3debdeb258b063b121775081932857f2f731330 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,19 @@ +version: "3.7" +services: + imagehoster: + build: + context: . + container_name: imagehoster + ports: + - 8800:8800 + - 3234:3234 +# volumes: +# - ./yarn.lock:/app/yarn.lock:delegated +# - ./package.json:/app/package.json:delegated +# - ./Makefile:/app/Makefile:delegated +# - ./test:/app/test:delegated +# - ./src:/app/src:delegated +# - ./config:/app/config:delegated +# - ./.git:/app/.git:delegated +# - ./tsconfig.json:/app/tsconfig.json:delegated +# - ./tslint.json:/app/tslint.json:delegated diff --git a/package.json b/package.json index 74e4c72174f81db452caa4fc6d577fdba88bf144..07a6de804512c614074f810dfe3f81703b3053e1 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "bunyan": "^2.0.2", "busboy": "^0.2.14", "config": "^1.29.4", + "fs-blob-store": "^6.0.0", "hivesigner": "^3.2.0", "koa": "^2.3.0", "koa-router": "^7.4.0", @@ -49,8 +50,8 @@ "mocha-junit-reporter": "^1.17.0", "nyc": "^14.1.1", "onchange": "^6.1.0", - "ts-node": "^7.0.0", + "ts-node": "^8.10.2", "tslint": "^5.16.0", - "typescript": "^2.7.1" + "typescript": "^3.9.6" } } diff --git a/src/common.ts b/src/common.ts index 25ace69fbd86f4e3dabcd5555e9a7fd9013733d1..d13f0c962f480a76345976b77c10a4264f4d5129 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,8 +1,8 @@ /** Misc shared instances. */ +import {Client} from '@hiveio/dhive' import {AbstractBlobStore} from 'abstract-blob-store' import * as config from 'config' -import {Client} from '@hiveio/dhive' import {IRouterContext} from 'koa-router' import * as Redis from 'redis' @@ -33,7 +33,10 @@ if (config.has('redis_url')) { let S3Client: any function loadStore(key: string): AbstractBlobStore { const conf = config.get(key) as any - if (conf.type === 'memory') { + if (conf.type === 'fs') { + logger.warn('using file store for %s', key) + return require('fs-blob-store')('/tmp') + } else if (conf.type === 'memory') { logger.warn('using memory store for %s', key) return require('abstract-blob-store')() } else if (conf.type === 's3') { diff --git a/src/routes.ts b/src/routes.ts index bf8f8dad6244730acbfc146526dc64f22511e2e9..fa5853a80541e5a40e5568abdf4f84be841d0ee9 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -7,7 +7,7 @@ import {KoaContext} from './common' import {legacyProxyHandler} from './legacy-proxy' import {proxyHandler} from './proxy' import {serveHandler} from './serve' -import {uploadHandler, uploadHsHandler} from './upload' +import {uploadCsHandler, uploadHandler, uploadHsHandler} from './upload' const version = require('./version') const router = new Router() @@ -24,6 +24,7 @@ router.get('/.well-known/healthcheck.json', healthcheck as any) router.get('/u/:username/avatar/:size?', avatarHandler as any) router.post('/hs/:accesstoken', uploadHsHandler as any) router.post('/:username/:signature', uploadHandler as any) +router.post('/cs/:username/:signature', uploadCsHandler as any) router.get('/:width(\\d+)x:height(\\d+)/:url(.*)', legacyProxyHandler as any) router.get('/p/:url', proxyHandler as any) router.get('/:hash/:filename?', serveHandler as any) diff --git a/src/upload.ts b/src/upload.ts index 5d47ef56165ae10d80852fe323c74ab6b2067bd5..7fd090433918d95d21c318e8ebbcf6a239c6cf3f 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -1,14 +1,15 @@ /** Uploads file to blob store. */ +import {Client, Signature} from '@hiveio/dhive' import * as Busboy from 'busboy' import * as config from 'config' import {createHash} from 'crypto' -import {Client, Signature} from '@hiveio/dhive' +// @ts-ignore +import * as hivesigner from 'hivesigner' import * as http from 'http' import * as multihash from 'multihashes' import * as RateLimiter from 'ratelimiter' import {URL} from 'url' -import * as hivesigner from 'hivesigner' import {accountBlacklist} from './blacklist' import {KoaContext, redisClient, rpcClient, uploadStore} from './common' @@ -78,13 +79,15 @@ async function getRatelimit(account: string) { }) }) } -const b64uLookup = { - '/': '_', _: '/', '+': '-', '-': '+', '=': '.', '.': '=', +const b64uLookup: Record<string, string> = { + '/': '_', '_': '/', '+': '-', '-': '+', '=': '.', '.': '=', } -function b64uToB64 (str: string) { - const tt = str.replace(/(-|_|\.)/g, function(m) { return b64uLookup[m]}) +function b64uToB64(str: string) { + const tt = str.replace(/(-|_|\.)/g, (m) => b64uLookup[m]) return tt } + +/** Handling upload with HiveSigner */ export async function uploadHsHandler(ctx: KoaContext) { ctx.tag({handler: 'hsupload'}) let validSignature = false @@ -112,7 +115,7 @@ export async function uploadHsHandler(ctx: KoaContext) { .update('ImageSigningChallenge') .update(data) .digest() - + const token = ctx.params['accesstoken'] const decoded = Buffer.from(b64uToB64(token), 'base64').toString() const tokenObj = JSON.parse(decoded) @@ -140,7 +143,7 @@ export async function uploadHsHandler(ctx: KoaContext) { accessToken: token, }) - await cl.me(function (err: any, res: any) { + await cl.me((err: any, res: any) => { if (!err && res) { account = res.account APIError.assert(account, APIError.Code.NoSuchAccount) @@ -152,18 +155,20 @@ export async function uploadHsHandler(ctx: KoaContext) { if (account && account.name) { ['posting', 'active', 'owner'].forEach((type) => { - account[type].account_auths.forEach((key: string[]) => { + // @ts-ignore + // tslint:disable-next-line:no-shadowed-variable + account[type].account_auths.forEach((key: string[]) => { if ( !validSignature && key[0] === UPLOAD_LIMITS.app_account ) { - validSignature = true; + validSignature = true } - }); - }); + }) + }) } } - }); + }) APIError.assert(validSignature, APIError.Code.InvalidSignature) APIError.assert(!accountBlacklist.includes(account.name), APIError.Code.Blacklisted) @@ -194,6 +199,104 @@ export async function uploadHsHandler(ctx: KoaContext) { ctx.body = {url} } } + +/** Handling upload by signing image checksum */ +export async function uploadCsHandler(ctx: KoaContext) { + ctx.tag({handler: 'upload'}) + + APIError.assert(ctx.method === 'POST', {code: APIError.Code.InvalidMethod}) + APIError.assertParams(ctx.params, ['username', 'signature']) + + let signature: Signature + try { + signature = Signature.fromString(ctx.params['signature']) + } catch (cause) { + throw new APIError({code: APIError.Code.InvalidSignature, cause}) + } + + APIError.assert(ctx.get('content-type').includes('multipart/form-data'), + {message: 'Only multipart uploads are supported'}) + + const contentLength = Number.parseInt(ctx.get('content-length')) + + APIError.assert(Number.isFinite(contentLength), + APIError.Code.LengthRequired) + + APIError.assert(contentLength <= MAX_IMAGE_SIZE, + APIError.Code.PayloadTooLarge) + + const file = await parseMultipart(ctx.req) + const fileData = await readStream(file.stream) + const fileHash = createHash('sha256') + .update(fileData) + .digest() + + // extra check if client manges to lie about the content-length + APIError.assert((file.stream as any).truncated !== true, + APIError.Code.PayloadTooLarge) + + const imageHash = createHash('sha256') + .update('ImageSigningChallenge') + .update(fileHash) + .digest() + + const [account] = await rpcClient.database.getAccounts([ctx.params['username']]) + APIError.assert(account, APIError.Code.NoSuchAccount) + + let validSignature = false + let publicKey + try { + publicKey = signature.recover(imageHash).toString() + } catch (cause) { + throw new APIError({code: APIError.Code.InvalidSignature, cause}) + } + + const thresholdPosting = account.posting.weight_threshold + for (const auth of account.posting.key_auths) { + if (auth[0] === publicKey && auth[1] >= thresholdPosting) { + validSignature = true + break + } + } + + const thresholdActive = account.active.weight_threshold + for (const auth of account.active.key_auths) { + if (auth[0] === publicKey && auth[1] >= thresholdActive) { + validSignature = true + break + } + } + + APIError.assert(validSignature, APIError.Code.InvalidSignature) + APIError.assert(!accountBlacklist.includes(account.name), APIError.Code.Blacklisted) + + let limit: RateLimit = {total: 0, remaining: Infinity, reset: 0} + try { + limit = await getRatelimit(account.name) + } catch (error) { + ctx.log.warn(error, 'unable to enforce upload rate limits') + } + + APIError.assert(limit.remaining > 0, APIError.Code.QoutaExceeded) + + APIError.assert(repLog10(account.reputation) >= UPLOAD_LIMITS.reputation, APIError.Code.Deplorable) + + const key = 'D' + multihash.toB58String(multihash.encode(imageHash, 'sha2-256')) + const url = new URL(`${ key }/${ file.name }`, SERVICE_URL) + + if (!(await storeExists(uploadStore, key))) { + await storeWrite(uploadStore, key, fileData) + } else { + ctx.log.debug('key %s already exists in store', key) + } + + ctx.log.info({uploader: account.name, size: fileData.byteLength}, 'image uploaded') + + ctx.status = 200 + ctx.body = {url} +} + +/** Handling upload by signing image data */ export async function uploadHandler(ctx: KoaContext) { ctx.tag({handler: 'upload'}) @@ -217,7 +320,6 @@ export async function uploadHandler(ctx: KoaContext) { APIError.assert(contentLength <= MAX_IMAGE_SIZE, APIError.Code.PayloadTooLarge) - const file = await parseMultipart(ctx.req) const data = await readStream(file.stream) @@ -236,11 +338,10 @@ export async function uploadHandler(ctx: KoaContext) { let validSignature = false let publicKey try { - publicKey = signature.recover(imageHash).toString() + publicKey = signature.recover(imageHash).toString() } catch (cause) { throw new APIError({code: APIError.Code.InvalidSignature, cause}) } - const thresholdPosting = account.posting.weight_threshold for (const auth of account.posting.key_auths) { if (auth[0] === publicKey && auth[1] >= thresholdPosting) { diff --git a/test/index.ts b/test/index.ts index cafeacdf47d49a7b51db973358c1e1d835034f1f..f2f7873118bb699a4109d81ffab9f1b845ad27cc 100644 --- a/test/index.ts +++ b/test/index.ts @@ -46,7 +46,7 @@ before(() => { _client.call = async (api: string, method: string, params = []) => { const apiMethod = `${ api }-${ method }` switch (apiMethod) { - case 'database_api-get_accounts': + case 'condenser_api-get_accounts': assert.equal(params.length, 1, 'can only mock single account lookups') return [mockAccounts[params[0]]] default: diff --git a/test/upload.ts b/test/upload.ts index e302f808b6a80f379469054d3f061ddd3f26ff2b..e0a1e31e930a2165a388fadd902290ef9ee9acf8 100644 --- a/test/upload.ts +++ b/test/upload.ts @@ -1,14 +1,12 @@ -import 'mocha' import * as assert from 'assert' +import * as crypto from 'crypto' +import * as fs from 'fs' import * as http from 'http' +import 'mocha' import * as needle from 'needle' import * as path from 'path' -import * as fs from 'fs' -import * as crypto from 'crypto' -import {PrivateKey} from '@hiveio/dhive' import {app} from './../src/app' -import {rpcClient} from './../src/common' import {testKeys} from './index' @@ -27,17 +25,23 @@ export async function uploadImage(data: Buffer, port: number) { }, } const signature = testKeys.foo.sign(hash).toString() - needle.post(`:${ port }/foo/${ signature }`, payload, {multipart: true}, function (error, response, body) { - if (error) { - reject(error) - } else { - resolve({response, body}) - } - }) + const url = `http://localhost:${ port }/foo/${ signature }` + needle.post( + url, + payload, + {multipart: true}, + (error, response, body) => { + if (error) { + reject(error) + } else { + resolve({response, body}) + } + }, + ) }) } -describe('upload', function() { +describe('upload', () => { const port = 63205 const server = http.createServer(app.callback()) diff --git a/yarn.lock b/yarn.lock index 7bb04ee4cdcfd205ffae025c896de43fc5c3684a..98f7ac69a59e06a30d7831609d97fecaa578847d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -406,6 +406,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -413,11 +418,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= - arrify@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -545,7 +545,7 @@ bs58@^4.0.1: dependencies: base-x "^3.0.2" -buffer-from@^1.0.0, buffer-from@^1.1.0: +buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -968,7 +968,7 @@ dicer@0.2.5: readable-stream "1.1.x" streamsearch "0.1.2" -diff@3.5.0, diff@^3.1.0: +diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== @@ -999,6 +999,16 @@ dtrace-provider@~0.8: dependencies: nan "^2.14.0" +duplexify@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0" + integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw== + dependencies: + end-of-stream "^1.4.1" + inherits "^2.0.3" + readable-stream "^3.1.1" + stream-shift "^1.0.0" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -1027,7 +1037,7 @@ encodeurl@^1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -1145,6 +1155,16 @@ from2@^2.0.3: inherits "^2.0.1" readable-stream "^2.0.0" +fs-blob-store@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/fs-blob-store/-/fs-blob-store-6.0.0.tgz#30b467f6f26d2a1d68720a9cede224605972ff14" + integrity sha512-oMdboCaw6kTRXi5lKfjpw7DO7maC+gwFmaef3DsYQTYHtOoTmCo13ZGH1GoqFaD81RW5qbaT9YKlP+OA4dzbdA== + dependencies: + duplexify "^4.1.1" + end-of-stream "^1.4.4" + lru-cache "^6.0.0" + mkdirp-classic "^0.5.3" + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -1672,6 +1692,13 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" @@ -1779,7 +1806,7 @@ minizlib@^1.2.1: dependencies: minipass "^2.9.0" -mkdirp-classic@^0.5.2: +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== @@ -2481,10 +2508,10 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -source-map-support@^0.5.6: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== +source-map-support@^0.5.17: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -2554,6 +2581,11 @@ stream-head@^1.1.0: dependencies: through2 "2.x" +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + streamsearch@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -2742,19 +2774,16 @@ tree-kill@^1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -ts-node@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" - integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== +ts-node@^8.10.2: + version "8.10.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" + integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== dependencies: - arrify "^1.0.0" - buffer-from "^1.1.0" - diff "^3.1.0" + arg "^4.1.0" + diff "^4.0.1" make-error "^1.1.1" - minimist "^1.2.0" - mkdirp "^0.5.1" - source-map-support "^0.5.6" - yn "^2.0.0" + source-map-support "^0.5.17" + yn "3.1.1" tslib@^1.8.0, tslib@^1.8.1: version "1.13.0" @@ -2812,10 +2841,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^2.7.1: - version "2.9.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" - integrity sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w== +typescript@^3.9.6: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== urijs@^1.19.0: version "1.19.2" @@ -2969,6 +2998,11 @@ yallist@^3.0.0, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yargs-parser@^13.0.0, yargs-parser@^13.1.2: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" @@ -2998,7 +3032,7 @@ ylru@^1.2.0: resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f" integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ== -yn@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" - integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==