From 33ae5ef1a6fc8e129b8466b7885f03c0e47bb9ec Mon Sep 17 00:00:00 2001 From: Richie Date: Thu, 12 Dec 2024 16:35:55 +0100 Subject: [PATCH] rewrite the whole codebase --- .changeset/README.md | 8 + .changeset/config.json | 11 + .github/workflows/dev.yml | 19 + .github/workflows/publish.yml | 37 +- .gitignore | 2 + .npmignore | 20 +- .swcrc.cjs.json | 12 + .swcrc.mjs.json | 12 + CHANGELOG.md | 7 + LICENCE | 7 - LICENSE | 19 + README.md | 75 +- bun.lockb | Bin 0 -> 158555 bytes docker-compose.yml | 6 + example/.env.dist | 1 - example/cat.jpeg | Bin 5271 -> 0 bytes example/testServer.ts | 93 -- nodemon.json | 10 - package.json | 50 +- patch-dist-dirs.sh | 13 + src/ClonedReadStream.ts | 25 - src/backends/local/file.ts | 12 + src/backends/local/local.test.ts | 21 + src/backends/local/memory.ts | 5 + src/backends/s3/create-s3-client.ts | 29 + src/backends/s3/localstack-s3.test.ts | 140 +++ src/backends/s3/mock-s3.test.ts | 94 ++ src/backends/s3/parse-s3-uri.test.ts | 108 ++ src/backends/s3/parse-s3-uri.ts | 92 ++ src/backends/s3/s3.ts | 120 ++ src/expressMiddleware.ts | 43 - src/index.ts | 1 - src/nafs.ts | 299 +---- tsconfig.build.json | 17 + tsconfig.json | 35 +- tslint.json | 19 - yarn.lock | 1493 ------------------------- 37 files changed, 906 insertions(+), 2049 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 .github/workflows/dev.yml create mode 100644 .swcrc.cjs.json create mode 100644 .swcrc.mjs.json create mode 100644 CHANGELOG.md delete mode 100644 LICENCE create mode 100644 LICENSE create mode 100755 bun.lockb create mode 100644 docker-compose.yml delete mode 100644 example/.env.dist delete mode 100644 example/cat.jpeg delete mode 100644 example/testServer.ts delete mode 100644 nodemon.json create mode 100755 patch-dist-dirs.sh delete mode 100644 src/ClonedReadStream.ts create mode 100644 src/backends/local/file.ts create mode 100644 src/backends/local/local.test.ts create mode 100644 src/backends/local/memory.ts create mode 100644 src/backends/s3/create-s3-client.ts create mode 100644 src/backends/s3/localstack-s3.test.ts create mode 100644 src/backends/s3/mock-s3.test.ts create mode 100644 src/backends/s3/parse-s3-uri.test.ts create mode 100644 src/backends/s3/parse-s3-uri.ts create mode 100644 src/backends/s3/s3.ts delete mode 100644 src/expressMiddleware.ts create mode 100644 tsconfig.build.json delete mode 100644 tslint.json delete mode 100644 yarn.lock diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..e5b6d8d --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,8 @@ +# Changesets + +Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works +with multi-package repos, or single-package repos to help you version and publish your code. You can +find the full documentation for it [in our repository](https://github.com/changesets/changesets) + +We have a quick list of common questions to get you started engaging with this project in +[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..c8fca74 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "restricted", + "baseBranch": "main", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 0000000..d124f25 --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,19 @@ +name: dev + +on: [pull_request] + +jobs: + build-and-test: + name: Build and Test + runs-on: ubuntu-latest + services: + localstack: + image: localstack/localstack:s3-latest + ports: + - 4566:4566 + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install + - run: bun test + - run: bun run build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 05950a5..e513ed5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,20 +1,31 @@ -name: Publish +name: publish on: - release: - types: [published] + push: + branches: + - main jobs: - build: + publish: + name: Publish runs-on: ubuntu-latest + services: + localstack: + image: localstack/localstack:s3-latest + ports: + - 4566:4566 steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 - with: - node-version: 12 - registry-url: https://registry.npmjs.org/ - - run: yarn install - - run: yarn run build - - run: npm publish --access public + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - run: bun install + - run: bun test + - run: bun run build + - run: | + echo '@ricsam:registry=https://registry.npmjs.org' >> .npmrc + echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' >> .npmrc + echo '//registry.npmjs.org/:always-auth=true' >> .npmrc + - name: changeset publish + run: npx changeset publish --access=public --registry=https://registry.npmjs.org/ env: - NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index d85040d..383ddd1 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,5 @@ index.d.ts index.js nafs.d.ts nafs.js + +types diff --git a/.npmignore b/.npmignore index d38c706..e855a0e 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,18 @@ -example -dist -nodemon.json -tslint.json +*.test.* .prettierrc +bun.lockb +CONTRIBUTING.md +CHANGELOG.md +tsconfig.build.json +tsconfig.json .github +.changeset +LICENSE +matchers.d.ts +.gitignore +bunfig.toml +patch-dist-dirs.sh +happydom.ts +renovate.json +testing-library.ts +docker-compose.yml diff --git a/.swcrc.cjs.json b/.swcrc.cjs.json new file mode 100644 index 0000000..7684173 --- /dev/null +++ b/.swcrc.cjs.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://swc.rs/schema.json", + "module": { + "type": "commonjs" + }, + "jsc": { + "target": "esnext", + "parser": { + "syntax": "typescript" + } + } +} diff --git a/.swcrc.mjs.json b/.swcrc.mjs.json new file mode 100644 index 0000000..1f45342 --- /dev/null +++ b/.swcrc.mjs.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://swc.rs/schema.json", + "module": { + "type": "es6" + }, + "jsc": { + "target": "esnext", + "parser": { + "syntax": "typescript" + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..153ea37 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# nafs + +## 0.1.0 + +### Minor Changes + +- rewrite all code diff --git a/LICENCE b/LICENCE deleted file mode 100644 index 0955737..0000000 --- a/LICENCE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2020 Richard Samuelsson - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9cf1062 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b270bda..3f97f1b 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,52 @@ # Node Active FS - nafs -``` +Nafs is an abstraction of the [Node fs API](https://nodejs.org/api/fs.html) where you can choose to point the fs to a remote location like s3, postgres, logstash, kibana or locally like the local filesystem or in memory. Check the [compatiblility table](#supported-file-system-methods) to see which parts of the fs API has been implemented for each backend. + +```bash npm install nafs -yarn add nafs ``` -### Example - -```js -const { nafs, expressMiddleware } = require('nafs'); -const express = require('express'); - -const localFs = nafs('file:///tmp/dev_storage'); -const remoteFs = nafs('s3://key:secret@us-east-1/bucket_name/some/path'); - -const app = express(); - -app.use('/local-files', expressMiddleware(localFs.createReadStream)); -app.use('/remote-files', expressMiddleware(remoteFs.createReadStream)); - -app.get('/', (req, res) => { - remoteFs.writeFile('/hello', 'Hello World').then(() => { - res.send('saved file to s3, check it out on /remote-files/hello or /read'); - }); -}); -app.get('/read', (req, res) => { - removeFs.readFile('/hello').then((file) => { - res.send(file); - }); -}); -``` ### Enable cache for remote data ```js -const remoteFs = nafs('s3://key:secret@us-east-1/bucket_name?cacheDir=/tmp/images'); +const remoteFs = nafs('s3://key:secret@us-east-1/bucket_name'); +const localFs = nafs('/tmp/some_folder'); -console.time('hello'); -await remoteFs.readFile('/hello') -console.timeEnd('hello'); /* 70 ms */ +await remoteFs.promises.writeFile('/hello', 'Hello World'); +await remoteFs.promises.readFile('/hello', 'utf8'); // Hello World -/* now cached */ -console.time('hello'); -await remoteFs.readFile('/hello') -console.timeEnd('hello'); /* 2 ms */ +await localFs.promises.writeFile('/hello', 'Hello World'); +await localFs.promises.readFile('/hello', 'utf8'); // Hello World ``` + + +## Supported File System Methods +| Method | File System | Memory | S3 | PostgreSQL | Logstash | Kibana | +|---------------------|-------------|--------|-------|------------|-----------|---------| +| `promises.readFile` | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| `promises.writeFile` | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| `promises.unlink` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.rmdir` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.mkdir` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.readdir` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.stat` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.lstat` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.chmod` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.chown` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.utimes` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.rename` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.copyFile` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.symlink` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.readlink` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.truncate` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `promises.access` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `createReadStream` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| `createWriteStream` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | + +✅ - Implemented +❌ - Not Implemented + +File System and Memory implementations are provided via memfs and linkfs, supporting full Node.js fs API compatibility. S3 implementation currently supports basic file reading and writing operations. PostgreSQL, Logstash, and Kibana implementations are planned for future development. + + diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..214d948ef27358e6109681cc9268cc204bee450d GIT binary patch literal 158555 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

VrbIQ^i>}Dfxvzt)S3|j?jjnQt$up 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 MKl={(f8c-r1J%b>AOHXW literal 0 HcmV?d00001 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..93806d7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,6 @@ +version: '3.8' +services: + s3: + image: localstack/localstack:s3-latest + ports: + - "4566:4566" diff --git a/example/.env.dist b/example/.env.dist deleted file mode 100644 index f58bd5c..0000000 --- a/example/.env.dist +++ /dev/null @@ -1 +0,0 @@ -AFS_S3=s3://ACCESS_KEY_ID:SECRET_ACCESS_KEY@eu-north-1/bucket-name/folder-in-bucket diff --git a/example/cat.jpeg b/example/cat.jpeg deleted file mode 100644 index d2a7c5b889f35e3cf330d9803fedd5e3c228764c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5271 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: A, b: B): [A, B]; -function tuple(...args: any[]) { - return args; -} - -const fetchPolicy: - | 'cache-first' - | 'cache-and-network' - | 'network-only' - | 'cache-only' - | 'no-cache' = 'cache-and-network'; - -const storageDir = path.join(__dirname, '../testData'); - -const fileAfs = nafs('file://' + storageDir); -const s3Afs = nafs( - `${process.env.AFS_S3}?cacheDir=${storageDir}&fetchPolicy=${fetchPolicy}` -); - -const app = express(); - -const serves = tuple(tuple('file', fileAfs), tuple('s3', s3Afs)); - -serves.forEach(([key, { createReadStream, writeFile, readFile }]) => { - app.use(`/static/${key}`, expressMiddleware(createReadStream)); - app.get(`/${key}/write/doc`, (req, res, next) => { - writeFile('/written.html', `

Written!

`) - .then(() => { - res.send( - `View written file!` - ); - }) - .catch(next); - }); - app.get(`/${key}/write/img`, (req, res, next) => { - const jpeg = fs.readFileSync(path.join(__dirname, 'cat.jpeg')); - writeFile('/cat.jpeg', jpeg) - .then(() => { - res.send(`View written file!`); - }) - .catch(next); - }); - app.get(`/${key}/read/img.jpeg`, (req, res, next) => { - readFile('/cat.jpeg') - .then((body: any) => { - res.type('jpg'); - res.send(body); - }) - .catch(next); - }); - app.get(`/${key}/404`, (req, res, next) => { - readFile('/cat-404.jpeg') - .then((body: any) => { - res.type('jpg'); - res.send(body); - }) - .catch(next); - }); -}); - -app.get('/', (req, res) => { - const links = serves - .flatMap(([key]) => { - return [ - `/${key}/write/doc`, - `/${key}/write/img`, - `/${key}/read/img.jpeg`, - `/${key}/404`, - ]; - }) - .join('
'); - - res.send(links); -}); - -app.listen(3000, () => { - console.log('Listening on port 3000'); -}); diff --git a/nodemon.json b/nodemon.json deleted file mode 100644 index e1c3193..0000000 --- a/nodemon.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "watch": [ - "src" - ], - "ext": "ts", - "ignore": [ - "src/**/*.spec.ts" - ], - "exec": "ts-node ./example/testServer.ts --compiler-options \"include\": [\"src\", \"example/testServer.ts\"]" -} diff --git a/package.json b/package.json index 8413f78..8c1d2e6 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,21 @@ { "name": "nafs", - "version": "0.0.12", + "version": "0.1.0", "description": "Node Active FS - nafs. File system abstraction to read/write files to AWS S3 or local directory. Includes middleware for usage with express.", - "main": "index.js", + "main": "dist/cjs/index.js", + "module": "dist/mjs/index.js", + "exports": { + ".": { + "import": "./dist/mjs/index.js", + "require": "./dist/cjs/index.js", + "types": "./types/index.d.ts" + } + }, + "types": "./types/index.d.ts", "scripts": { - "start": "nodemon", - "build": "tsc && mv ./dist/* ./" + "start": "bun run src/index.ts", + "test": "bun test", + "build": "rm -rf ./dist ./types && tsc -p tsconfig.build.json && swc ./src --config-file .swcrc.cjs.json --out-dir ./dist/cjs --ignore **/*.test.ts --strip-leading-paths && swc ./src --config-file .swcrc.mjs.json --out-dir ./dist/mjs --ignore **/*.test.ts --strip-leading-paths && ./patch-dist-dirs.sh" }, "keywords": [ "fs", @@ -16,26 +26,20 @@ "express", "middleware" ], - "author": "Richard Samuelsson", + "author": "Richie ", "license": "MIT", "devDependencies": { - "@types/express": "^4.17.3", - "@types/mkdirp": "^1.0.0", - "@types/node": "^13.9.0", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "nodemon": "^2.0.2", - "prettier": "^1.19.1", - "ts-node": "^8.6.2", - "tslint": "^6.1.1", - "tslint-config-prettier": "^1.18.0", - "typescript": "^3.8.3" - }, - "dependencies": { + "dotenv": "16.4.5", + "@types/bun": "latest", "aws-sdk": "^2.658.0", - "mkdirp": "^1.0.4", - "path": "^0.12.7", - "query-string": "^6.11.1", - "url": "^0.11.0" - } + "memfs": "^4.14.0", + "@ricsam/linkfs": "^2.0.8", + "@aws-sdk/client-s3": "^3.701.0", + "prettier": "^3.4.1", + "@changesets/cli": "2.27.10", + "typescript": "^5.7.2", + "@swc/cli": "^0.5.2", + "@swc/core": "^1.10.1" + }, + "dependencies": {} } diff --git a/patch-dist-dirs.sh b/patch-dist-dirs.sh new file mode 100755 index 0000000..23a5e5b --- /dev/null +++ b/patch-dist-dirs.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +cat >dist/cjs/package.json <dist/mjs/package.json < { - this.push(chunk); - }); - - readableStream.on('end', () => { - this.push(null); - }); - - readableStream.on('error', (err) => { - this.emit('error', err); - }); - } - _read() { - /* */ - } -} diff --git a/src/backends/local/file.ts b/src/backends/local/file.ts new file mode 100644 index 0000000..406ac65 --- /dev/null +++ b/src/backends/local/file.ts @@ -0,0 +1,12 @@ +import { link } from '@ricsam/linkfs'; +import type { IFs } from 'memfs'; +import * as path from 'path'; +import * as realFs from 'fs'; + +export const createFileFs = (uri: string, fs?: IFs): IFs | typeof realFs => { + const lfs = link(fs ?? realFs, [ + '/', + uri.startsWith('/') ? uri : path.join(process.cwd(), uri), + ]); + return lfs; +}; diff --git a/src/backends/local/local.test.ts b/src/backends/local/local.test.ts new file mode 100644 index 0000000..43e79f9 --- /dev/null +++ b/src/backends/local/local.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from 'bun:test'; +import { createFsFromVolume, memfs, Volume } from 'memfs'; +import { nafs } from '../../nafs'; + +test('memfs', async () => { + const fs = await nafs(':memory:'); + await fs.promises.writeFile('/test', 'test'); + expect(await fs.promises.readFile('/test', 'utf-8')).toBe('test'); +}); + +test('fs', async () => { + const vol = new Volume(); + const realFs = createFsFromVolume(vol); + const fs = await nafs('file:///tmp', { + fs: realFs, + }); + await fs.promises.mkdir('/tmp', { recursive: true }); + await fs.promises.writeFile('/tmp/test', 'test'); + expect(await fs.promises.readFile('/tmp/test', 'utf-8')).toBe('test'); + expect(await realFs.promises.readFile('/tmp/tmp/test', 'utf-8')).toBe('test'); +}); diff --git a/src/backends/local/memory.ts b/src/backends/local/memory.ts new file mode 100644 index 0000000..a8493b1 --- /dev/null +++ b/src/backends/local/memory.ts @@ -0,0 +1,5 @@ +import type { IFs } from 'memfs'; +import { fs } from 'memfs'; +export const createMemFs = (): IFs => { + return fs; +}; diff --git a/src/backends/s3/create-s3-client.ts b/src/backends/s3/create-s3-client.ts new file mode 100644 index 0000000..8371545 --- /dev/null +++ b/src/backends/s3/create-s3-client.ts @@ -0,0 +1,29 @@ +import { S3Client, type S3ClientConfig } from "@aws-sdk/client-s3"; +import { parseS3Uri } from "./parse-s3-uri"; + +export function createS3Client(uri: string): S3Client { + const parsed = parseS3Uri(uri); + const config: S3ClientConfig = {}; + + // Region precedence: URI > Environment Variable > Default + if (parsed.region) { + config.region = parsed.region; + } else if (process.env.AWS_DEFAULT_REGION) { + config.region = process.env.AWS_DEFAULT_REGION; + } + + // Credentials precedence: URI > Environment Variables + const accessKeyId = parsed.credentials?.accessKeyId ?? process.env.AWS_ACCESS_KEY_ID; + const secretAccessKey = parsed.credentials?.secretAccessKey ?? process.env.AWS_SECRET_ACCESS_KEY; + + if (accessKeyId && secretAccessKey) { + config.credentials = { + accessKeyId, + secretAccessKey, + }; + } + + config.endpoint = parsed.endpoint; + + return new S3Client(config); +} diff --git a/src/backends/s3/localstack-s3.test.ts b/src/backends/s3/localstack-s3.test.ts new file mode 100644 index 0000000..8984c86 --- /dev/null +++ b/src/backends/s3/localstack-s3.test.ts @@ -0,0 +1,140 @@ +import { + CreateBucketCommand, + GetObjectCommand, + ListObjectsV2Command, + S3Client, +} from '@aws-sdk/client-s3'; +import { beforeAll, describe, expect, it } from 'bun:test'; +import { createS3Fs } from './s3'; + +describe('s3Fs LocalStack integration', () => { + const LOCALSTACK_ENDPOINT = 'http://127.0.0.1:4566'; + const BUCKET_NAME = 'test-bucket'; + let s3Client: S3Client; + + beforeAll(async () => { + process.env.AWS_DEFAULT_REGION = 'us-east-1'; + process.env.AWS_ACCESS_KEY_ID = 'test'; + process.env.AWS_SECRET_ACCESS_KEY = 'test'; + process.env.AWS_ENDPOINT_URL = LOCALSTACK_ENDPOINT; + + // Create a client for test verification + s3Client = new S3Client({ + endpoint: LOCALSTACK_ENDPOINT, + region: 'us-east-1', + credentials: { + accessKeyId: 'test', + secretAccessKey: 'test', + }, + forcePathStyle: true, + }); + + // Create test bucket + try { + await s3Client.send( + new CreateBucketCommand({ + Bucket: BUCKET_NAME, + }) + ); + } catch (err) { + // Bucket might already exist + } + }); + + async function listObjects(prefix?: string) { + const command = new ListObjectsV2Command({ + Bucket: BUCKET_NAME, + Prefix: prefix, + }); + const response = await s3Client.send(command); + return response.Contents?.map((obj) => obj.Key) || []; + } + + async function getObjectContent(key: string): Promise { + const command = new GetObjectCommand({ + Bucket: BUCKET_NAME, + Key: key, + }); + const response = await s3Client.send(command); + return await response.Body!.transformToString(); + } + + describe('root bucket operations', () => { + it('should write files to root when no prefix provided', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}`); + await s3fs.promises.writeFile('test.txt', 'Hello World'); + + const objects = await listObjects(); + expect(objects).toContain('test.txt'); + + const content = await getObjectContent('test.txt'); + expect(content).toBe('Hello World'); + }); + + it('should write files to root with trailing slash', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}/`); + await s3fs.promises.writeFile('root.txt', 'Root file'); + + const objects = await listObjects(); + expect(objects).toContain('root.txt'); + }); + }); + + describe('prefixed operations', () => { + const PREFIX = 'my/prefix'; + + it('should write files under specified prefix', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}/${PREFIX}`); + await s3fs.promises.writeFile('nested/file.txt', 'Nested content'); + + const objects = await listObjects(PREFIX); + expect(objects).toContain(`${PREFIX}/nested/file.txt`); + + const content = await getObjectContent(`${PREFIX}/nested/file.txt`); + expect(content).toBe('Nested content'); + }); + + it('should handle leading slashes in paths correctly', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}/${PREFIX}`); + await s3fs.promises.writeFile('/absolute/path.txt', 'Absolute path'); + + const objects = await listObjects(PREFIX); + expect(objects).toContain(`${PREFIX}/absolute/path.txt`); + }); + }); + + describe('read operations', () => { + it('should read previously written files', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}/read-test`); + const testContent = 'Test content for reading'; + + await s3fs.promises.writeFile('read.txt', testContent); + const content = await s3fs.promises.readFile('read.txt', 'utf8'); + + expect(content).toBe(testContent); + }); + + it('should handle binary files', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}/binary`); + const binaryData = new Uint8Array([1, 2, 3, 4, 5]); + + await s3fs.promises.writeFile('binary.dat', binaryData, 'binary'); + const content = await s3fs.promises.readFile('binary.dat'); + + expect(Buffer.isBuffer(content)).toBe(true); + expect(content).toEqual(Buffer.from(binaryData)); + }); + }); + + describe('error cases', () => { + it('should handle reading non-existent files', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}`); + expect(s3fs.promises.readFile('non-existent.txt')).rejects.toThrow(); + }); + + it('should handle invalid paths', async () => { + const s3fs = createS3Fs(`s3://${BUCKET_NAME}`); + expect(s3fs.promises.writeFile('', 'content')).rejects.toThrow(); + }); + }); +}); diff --git a/src/backends/s3/mock-s3.test.ts b/src/backends/s3/mock-s3.test.ts new file mode 100644 index 0000000..cdb4cfb --- /dev/null +++ b/src/backends/s3/mock-s3.test.ts @@ -0,0 +1,94 @@ +import { + afterEach, + beforeEach, + describe, + expect, + it, + jest, + mock, +} from 'bun:test'; +import { createS3Fs } from './s3'; + +mock.module('./create-s3-client', () => { + const fn = jest.fn(); + return { createS3Client: fn }; +}); + +describe('s3Fs with mocked client', () => { + let mockSend: jest.Mock; + beforeEach(async () => { + const { createS3Client } = await import('./create-s3-client'); + mockSend = jest.fn(); + (createS3Client as jest.Mock).mockReturnValue({ + send: mockSend, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should write a text file', async () => { + const s3fs = createS3Fs('s3://test-bucket/prefix'); + mockSend.mockResolvedValueOnce({}); + + await s3fs.promises.writeFile('test.txt', 'Hello World', 'utf8'); + + expect(mockSend).toHaveBeenCalledWith( + expect.objectContaining({ + input: { + Bucket: 'test-bucket', + Key: 'prefix/test.txt', + Body: expect.any(Buffer), + ContentLength: 11, + }, + }) + ); + }); + + it('should read a text file', async () => { + const s3fs = createS3Fs('s3://test-bucket/prefix'); + const mockBody = { + transformToString: jest.fn().mockResolvedValue('Hello World'), + transformToByteArray: jest + .fn() + .mockResolvedValue(new Uint8Array(Buffer.from('Hello World'))), + }; + + mockSend.mockResolvedValueOnce({ + Body: mockBody, + }); + + const result = await s3fs.promises.readFile('test.txt', 'utf8'); + expect(result).toBe('Hello World'); + expect(mockBody.transformToString).toHaveBeenCalledWith('utf8'); + }); + + it('should handle paths correctly', async () => { + const s3fs = createS3Fs('s3://test-bucket/base/path'); + mockSend.mockResolvedValueOnce({}); + + await s3fs.promises.writeFile('/some/nested/file.txt', 'content'); + + expect(mockSend).toHaveBeenCalledTimes(1); + expect(mockSend).toHaveBeenCalledWith( + expect.objectContaining({ + input: { + Bucket: 'test-bucket', + Key: 'base/path/some/nested/file.txt', + Body: expect.any(Buffer), + ContentLength: 7, + }, + }) + ); + }); + + it('should handle errors', async () => { + const s3fs = createS3Fs('s3://test-bucket'); + mockSend.mockRejectedValueOnce(new Error('S3 error')); + + expect(s3fs.promises.readFile('non-existent.txt')).rejects.toThrow( + 'Error reading file from S3: S3 error' + ); + }); +}); diff --git a/src/backends/s3/parse-s3-uri.test.ts b/src/backends/s3/parse-s3-uri.test.ts new file mode 100644 index 0000000..424a0d5 --- /dev/null +++ b/src/backends/s3/parse-s3-uri.test.ts @@ -0,0 +1,108 @@ +import { parseS3Uri } from './parse-s3-uri'; +import { describe, expect, it, test } from 'bun:test'; + +describe('parseS3Uri', () => { + describe('root paths', () => { + it('should handle bucket without trailing slash', () => { + const uri = 's3://bucket-name'; + expect(parseS3Uri(uri)).toEqual({ + bucket: 'bucket-name', + key: '', + }); + }); + + it('should handle bucket with trailing slash', () => { + const uri = 's3://bucket-name/'; + expect(parseS3Uri(uri)).toEqual({ + bucket: 'bucket-name', + key: '', + }); + }); + + it('should handle root paths with credentials', () => { + const uri = 's3://key:secret@us-east-1/bucket-name'; + expect(parseS3Uri(uri)).toEqual({ + credentials: { + accessKeyId: 'key', + secretAccessKey: 'secret', + }, + region: 'us-east-1', + bucket: 'bucket-name', + key: '', + }); + }); + + it('should handle root paths with region', () => { + const uri = 's3://us-east-1/bucket-name'; + expect(parseS3Uri(uri)).toEqual({ + region: 'us-east-1', + bucket: 'bucket-name', + key: '', + }); + }); + }); + + describe('full format (credentials + region)', () => { + it('should parse URI with credentials and region', () => { + const uri = 's3://accessKey:secretKey@us-east-1/bucket-name/some/path'; + expect(parseS3Uri(uri)).toEqual({ + credentials: { + accessKeyId: 'accessKey', + secretAccessKey: 'secretKey', + }, + region: 'us-east-1', + bucket: 'bucket-name', + key: 'some/path', + }); + }); + + it('should handle special characters in credentials', () => { + const uri = 's3://access+key:secret/key+123@us-east-1/bucket-name/path'; + expect(parseS3Uri(uri)).toEqual({ + credentials: { + accessKeyId: 'access+key', + secretAccessKey: 'secret/key+123', + }, + region: 'us-east-1', + bucket: 'bucket-name', + key: 'path', + }); + }); + }); + + describe('region format', () => { + it('should parse URI with region', () => { + const uri = 's3://us-east-1/bucket-name/some/path'; + expect(parseS3Uri(uri)).toEqual({ + region: 'us-east-1', + bucket: 'bucket-name', + key: 'some/path', + }); + }); + }); + + describe('simple format', () => { + it('should parse URI with just bucket and path', () => { + const uri = 's3://bucket-name/some/path'; + expect(parseS3Uri(uri)).toEqual({ + bucket: 'bucket-name', + key: 'some/path', + }); + }); + }); + + describe('error cases', () => { + const invalidUris = [ + 's3://', + 'not-s3://bucket/path', + 'http://bucket/path', + 's3://key@region/bucket/path', // missing secret + 's3://key:@region/bucket/path', // empty secret + 's3://@region/bucket/path', // missing credentials + ]; + + test.each(invalidUris)('should throw for invalid URI: %s', (uri) => { + expect(() => parseS3Uri(uri)).toThrow('Invalid S3 URI format'); + }); + }); +}); diff --git a/src/backends/s3/parse-s3-uri.ts b/src/backends/s3/parse-s3-uri.ts new file mode 100644 index 0000000..22e9ce6 --- /dev/null +++ b/src/backends/s3/parse-s3-uri.ts @@ -0,0 +1,92 @@ +export interface ParsedS3Uri { + region?: string; + bucket: string; + key: string; + credentials?: { + accessKeyId: string; + secretAccessKey?: string; + }; + endpoint?: string; +} + +type UriFormat = + | 'full' // s3://key:secret@region/bucket/path + | 'region' // s3://region/bucket/path + | 'simple'; // s3://bucket/path + +export function parseS3Uri(uri: string): ParsedS3Uri { + // Split URI and query params + const [baseUri, queryString] = uri.split('?'); + + // Parse query params if present + const endpoint = + queryString + ?.split('&') + .map((param) => param.split('=')) + .find(([key]) => key === 'endpoint')?.[1] ?? + process.env.AWS_ENDPOINT_URL_S3 ?? + process.env.AWS_ENDPOINT_URL; + + if (!baseUri.startsWith('s3://')) { + throw new Error('Invalid S3 URI format'); + } + + // Remove s3:// prefix + const path = baseUri.slice(5); + + // Determine URI format + const format: UriFormat = path.includes('@') + ? 'full' + : path.split('/')[0].match(/^[a-z]+-[a-z]+-\d+$/) + ? 'region' + : 'simple'; + + switch (format) { + case 'full': { + const [credentials, remainder] = path.split('@'); + const [accessKeyId, secretAccessKey] = credentials.split(':'); + const [region, bucket, ...keyParts] = remainder.split('/'); + + if (!accessKeyId || !secretAccessKey || !region || !bucket) { + throw new Error('Invalid S3 URI format'); + } + + return { + credentials: { accessKeyId, secretAccessKey }, + region, + bucket, + key: keyParts.join('/') || '', + ...(endpoint && { endpoint: decodeURIComponent(endpoint) }), + }; + } + + case 'region': { + const [region, bucket, ...keyParts] = path.split('/'); + + if (!region || !bucket) { + throw new Error('Invalid S3 URI format'); + } + + return { + region, + bucket, + key: keyParts.join('/') || '', + ...(endpoint && { endpoint: decodeURIComponent(endpoint) }), + }; + } + + case 'simple': { + const [bucket, ...keyParts] = path.split('/'); + + if (!bucket) { + throw new Error('Invalid S3 URI format'); + } + + return { + bucket, + key: keyParts.join('/') || '', + ...(endpoint && { endpoint: decodeURIComponent(endpoint) }), + }; + } + } +} diff --git a/src/backends/s3/s3.ts b/src/backends/s3/s3.ts new file mode 100644 index 0000000..d030646 --- /dev/null +++ b/src/backends/s3/s3.ts @@ -0,0 +1,120 @@ +import { GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3'; +import { parseS3Uri } from './parse-s3-uri'; +import { createS3Client } from './create-s3-client'; + +type ReadFileType = typeof import('fs').promises.readFile; +type WriteFileType = typeof import('fs').promises.writeFile; + +type ArgType< + F extends (...args: any) => any, + N extends number, +> = Parameters[N]; + +type s3Fs = { + promises: { + readFile: ReadFileType; + writeFile: WriteFileType; + }; +}; + +export const createS3Fs = (bucketUri: string): s3Fs => { + const client = createS3Client(bucketUri); + const parsed = parseS3Uri(bucketUri); + const basePath = parsed.key; + + const normalizePath = (path: string): { bucket: string; key: string } => { + // Remove leading slash if present + const normalizedPath = path.startsWith('/') ? path.slice(1) : path; + + // Combine base path with provided path, avoiding double slashes + const fullPath = basePath + ? `${basePath.replace(/\/$/, '')}/${normalizedPath}` + : normalizedPath; + + return { + bucket: parsed.bucket, + key: fullPath, + }; + }; + + const readFile = async ( + path: ArgType, + options?: ArgType + ): Promise => { + const pathStr = path.toString(); + const { bucket, key } = normalizePath(pathStr); + + const command = new GetObjectCommand({ + Bucket: bucket, + Key: key, + }); + + try { + const response = await client.send(command); + + if (!response.Body) { + throw new Error('Empty response body'); + } + + // Convert the readable stream to a buffer + const stream = response.Body; + + // Handle encoding if specified + if (typeof options === 'string') { + return stream.transformToString(options); + } else if (options?.encoding) { + return stream.transformToString(options.encoding); + } + + return Buffer.from(await stream.transformToByteArray()); + } catch (error) { + // Transform AWS errors to match fs.readFile error format + const err = error instanceof Error ? error : new Error('Unknown error'); + err.message = `Error reading file from S3: ${err.message}`; + throw err; + } + }; + + const writeFile = async ( + path: ArgType, + data: ArgType, + options?: ArgType + ): Promise => { + const pathStr = path.toString(); + const { bucket, key } = normalizePath(pathStr); + + let buffer: Buffer; + if (typeof data === 'string') { + const encoding = + typeof options === 'string' ? options : options?.encoding; + + buffer = Buffer.from(data, encoding ?? 'utf8'); + } else { + buffer = Buffer.from(data as Uint8Array); + } + + const command = new PutObjectCommand({ + Bucket: bucket, + Key: key, + Body: buffer, + ContentLength: buffer.length, + }); + + try { + await client.send(command); + } catch (error) { + // Transform AWS errors to match fs.writeFile error format + const err = error instanceof Error ? error : new Error('Unknown error'); + err.message = `Error writing file to S3: ${err.message}`; + throw err; + } + }; + + return { + promises: { + // because return type overloads are not the same as the union return type + readFile: readFile as ReadFileType, + writeFile, + }, + }; +}; diff --git a/src/expressMiddleware.ts b/src/expressMiddleware.ts deleted file mode 100644 index ee2dc9f..0000000 --- a/src/expressMiddleware.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { IncomingMessage, OutgoingMessage } from 'http'; -import * as stream from 'stream'; -import { parse } from 'url'; -import { ClonedReadStream } from './ClonedReadStream'; - -export const expressMiddleware = ( - createReadStream: (fpath: string) => stream.Readable -) => { - return ( - req: IncomingMessage, - res: OutgoingMessage, - next: (err?: any) => void - ): any => { - if (req.method !== 'GET' && req.method !== 'HEAD') { - /* exists on express req */ - (res as any).statusCode = 405; - res.setHeader('Allow', 'GET, HEAD'); - res.setHeader('Content-Length', '0'); - res.end(); - next(); - return; - } - const fpath = parse(req.url as string).pathname; - - if (!fpath) { - next(new Error('Invalid path')); - return; - } - - const stream = createReadStream(fpath); - stream.once('error', function error(err) { - // next(err); - if (err.name === 'NoSuchKey') { - (res as any).statusCode = 404; - res.write('Not found'); - res.end(); - } else { - next(err); - } - }); - new ClonedReadStream(stream).pipe(res); - }; -}; diff --git a/src/index.ts b/src/index.ts index efbfae0..7e6bc8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,2 +1 @@ -export { expressMiddleware } from './expressMiddleware'; export { nafs } from './nafs'; diff --git a/src/nafs.ts b/src/nafs.ts index f93f917..4e3d476 100644 --- a/src/nafs.ts +++ b/src/nafs.ts @@ -1,287 +1,48 @@ -import * as AWS from 'aws-sdk'; -import * as fs from 'fs'; -import mkdirp from 'mkdirp'; -import * as path from 'path'; -import * as queryString from 'query-string'; -import * as stream from 'stream'; -import { ClonedReadStream } from './ClonedReadStream'; - - - -type NAFS = { - writeFile: (fpath: string, body: any) => Promise; - readFile: (fpath: string) => Promise; - createReadStream: (fpath: string) => stream.Readable; - createWriteStream: (fpath: string) => stream.Writable; -}; - -type NAFSFactory = (url: string) => NAFS; - -const streamToPromise = (stream: stream) => { - return new Promise((resolve, reject) => { - const chunks: any[] = []; - stream.on('data', (chunk) => { - chunks.push(chunk); - }); - - stream.on('end', () => { - const body = Buffer.concat(chunks); - resolve(body); - }); - - stream.on('error', (error) => { - reject(error); - }); - }); -}; - -const activeStorageFileServe: NAFSFactory = (url: string) => { - let rootPath: string; - const r = url.match(/^file:\/\/(.*)$/); - if (r) { - rootPath = r[1]; - } else { - throw new Error('Invalid URL'); - } - - const createReadStream = (fpath: string) => - fs.createReadStream(path.join(rootPath, fpath)); - const createWriteStream = (fpath: string) => - fs.createWriteStream(path.join(rootPath, fpath)); - - return { - createReadStream, - writeFile: (fpath: string, fileContent: any) => - new Promise((resolve, reject) => { - fs.writeFile(path.join(rootPath, fpath), fileContent, (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }), - readFile: (fpath: string) => - new Promise((resolve, reject) => { - fs.readFile(path.join(rootPath, fpath), (err, file) => { - if (err) { - reject(err); - } else { - resolve(file); - } - }); - }), - createWriteStream, +import type { IFs } from 'memfs'; + +type NAFSOptions = { + /** + * only used when the storage is a file:// + */ + fs?: IFs; + /** + * only used when the storage is a s3:// + */ + s3?: { + accessKeyId: string; + secretAccessKey: string; + region: string; }; }; -// pass a into b -const forwardStream = (a: stream.Readable, b: stream.PassThrough) => { - const readStream = new ClonedReadStream(a); - readStream.pipe(b); - readStream.on('close', () => { - b.emit('close'); - }); - readStream.on('end', () => { - b.emit('end'); - }); - readStream.on('error', (err) => { - b.emit('error', err); - }); -}; - -const activeStorageS3Serve: NAFSFactory = (url) => { - let apiKey: string; - let secret: string; - let region: string; - let bucket: string; - let rootPath: string; - let cacheDir: string | undefined; - - let fetchPolicy: - | 'cache-first' - | 'cache-and-network' - | 'network-only' - | 'cache-only' - | 'no-cache' = 'cache-first'; - - const r = url.match( - /^s3:\/\/([^:]+):([^@]+)@([^/]+)\/([^/?]+)\/?([^?]*)(\?.+)?$/ - ); - - if (r) { - let queryParams: string; - [, apiKey, secret, region, bucket, rootPath = '/', queryParams] = r; - if (queryParams) { - ({ cacheDir, fetchPolicy } = queryString.parse(queryParams) as any); - } - } else { - throw new Error( - 'Invalid URL, should conform to s3://key:secret@eu-central-1/bucket_name/some/path/in/bucket?cacheDir=/tmp/img_data&fetchPolicy=network-only' - ); - } - - const s3 = new AWS.S3({ - accessKeyId: apiKey, - secretAccessKey: secret, - region, - }); - - const getS3Path = (fpath: string) => - path.join(rootPath, fpath).replace(/^\//, ''); - - const readRemoteStream = (fpath: string) => { - return s3 - .getObject({ - Bucket: bucket, - Key: getS3Path(fpath), - }) - .createReadStream(); - }; - - let cachePath: string | undefined; - if (cacheDir) { - cachePath = path.resolve(path.join(cacheDir, rootPath)); +export const nafs = async ( + storage: string, + options?: NAFSOptions +): Promise => { + if (storage === ':memory:') { + const { createMemFs } = await import('./backends/local/memory'); + return createMemFs(); } - const writeFileToCache = async (fpath: string, body: any) => { - if (cachePath) { - const cacheFpath = path.join(cachePath, fpath); - await mkdirp(path.dirname(cacheFpath)); - return fs.promises.writeFile(cacheFpath, body); - } - }; - - const writeStreamToCache = async ( - fpath: string, - readStream: stream.Readable - ) => { - if (cachePath) { - const cacheFpath = path.join(cachePath, fpath); - await mkdirp(path.dirname(cacheFpath)); - const fstream = fs.createWriteStream(cacheFpath); - const clonedReadStream = new ClonedReadStream(readStream); - clonedReadStream.pipe(fstream); - return new Promise((resolve, reject) => { - const onError = (err: any) => { - fs.unlink(cacheFpath, () => { - reject(err); - }); - }; - clonedReadStream.once('error', onError); - fstream.once('end', () => { - resolve(); - }); - fstream.once('error', onError); - }); - } - }; - - const createReadStream = (fpath: string) => { - const passStream = new stream.PassThrough(); - if (cachePath) { - const cacheFpath = path.join(cachePath, fpath); - if (fetchPolicy === 'cache-first') { - fs.promises - .access(cacheFpath) - .then(() => { - forwardStream(fs.createReadStream(cacheFpath), passStream); - }) - .catch(() => { - const remoteStream = readRemoteStream(fpath); - writeStreamToCache(fpath, remoteStream).catch((err) => { - throw err; - }); - forwardStream(remoteStream, passStream); - }); - } else if (fetchPolicy === 'cache-and-network') { - const remoteStream = readRemoteStream(fpath); - writeStreamToCache(fpath, remoteStream).catch((err) => { - // ignore error here - }); - fs.promises - .access(cacheFpath) - .then(() => { - forwardStream(fs.createReadStream(cacheFpath), passStream); - }) - .catch((err) => { - forwardStream(remoteStream, passStream); - }); - } else if (fetchPolicy === 'cache-only') { - return fs.createReadStream(cacheFpath); - } else if (fetchPolicy === 'network-only') { - const stream = readRemoteStream(fpath); - writeStreamToCache(fpath, stream).catch((err) => { - // ignore error here - }); - forwardStream(stream, passStream); - } else if (fetchPolicy === 'no-cache') { - return readRemoteStream(fpath); - } - } else { - return readRemoteStream(fpath); - } - return passStream; - }; - - const createWriteStream = (fpath: string) => { - const ptStream = new stream.PassThrough(); - const s3Stream = new ClonedReadStream(ptStream); - const cacheStream = new ClonedReadStream(ptStream); - s3.upload( - { - Bucket: bucket, - Key: getS3Path(fpath), - Body: s3Stream, - }, - (err) => { - if (err) { - throw err; - } - } - ); - writeStreamToCache(fpath, cacheStream); - return ptStream; - }; - - return { - readFile: async (fpath) => { - const stream = createReadStream(fpath); - return streamToPromise(stream); - }, - writeFile: async (fpath, body) => { - await Promise.all([ - writeFileToCache(fpath, body), - s3 - .putObject({ - Bucket: bucket, - Key: getS3Path(fpath), - Body: body, - }) - .promise(), - ]); - }, - createReadStream, - createWriteStream, - }; -}; - -export const nafs: NAFSFactory = (url: string) => { let proto: string; - - const r = url.match(/([^:])+(?=:\/\/)/); + let uri: string; + const r = storage.match(/^([^:]+):\/\/(.+)/); if (r) { - proto = r[0]; + proto = r[1]; + uri = r[2]; } else { throw new Error('Invalid url'); } switch (proto) { case 'file': { - return activeStorageFileServe(url); + const { createFileFs } = await import('./backends/local/file'); + const lfs = createFileFs(uri, options?.fs); + return lfs as any; } case 's3': { - return activeStorageS3Serve(url); + const { createS3Fs } = await import('./backends/s3/s3'); + return createS3Fs(uri) as any; } default: { throw new Error('Invalid url supplied, the protocol is not supported'); diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..a7b0837 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowJs": false, + "noEmit": false, + "isolatedDeclarations": true, + "emitDeclarationOnly": true, + "declaration": true, + "outDir": "types" + }, + "include": [ + "src", + ], + "exclude": [ + "*.test.*" + ] +} diff --git a/tsconfig.json b/tsconfig.json index dbe5676..f922565 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,27 @@ { "compilerOptions": { - "lib": [ - "esnext" - ], - "target": "esnext", + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": false, + "noEmit": true, + + // Best practices "strict": true, - "declaration": true, - "allowSyntheticDefaultImports": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, "noUnusedParameters": false, - "module": "commonjs", - "esModuleInterop": true, - "outDir": "dist" - }, - "include": [ - "src" - ] + "noPropertyAccessFromIndexSignature": false + } } diff --git a/tslint.json b/tslint.json deleted file mode 100644 index b3ef5e5..0000000 --- a/tslint.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": ["tslint:recommended", "tslint-config-prettier"], - "jsRules": {}, - "rules": { - "no-console": false, - "no-submodule-imports": [false], - "object-literal-sort-keys": false, - "interface-name": false, - "interface-over-type-literal": false, - "max-classes-per-file": false, - "triple-equals": false, - "no-shadowed-variable": false - }, - "rulesDirectory": [], - "linterOptions": { - "exclude": ["prisma/generated/**"] - } -} diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 82f0b76..0000000 --- a/yarn.lock +++ /dev/null @@ -1,1493 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@^7.0.0": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" - integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - dependencies: - "@babel/highlight" "^7.8.3" - -"@babel/highlight@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" - integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^4.0.0" - -"@types/body-parser@*": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" - integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.33" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" - integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== - dependencies: - "@types/node" "*" - -"@types/express-serve-static-core@*": - version "4.17.2" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz#f6f41fa35d42e79dbf6610eccbb2637e6008a0cf" - integrity sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg== - dependencies: - "@types/node" "*" - "@types/range-parser" "*" - -"@types/express@^4.17.3": - version "4.17.3" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.3.tgz#38e4458ce2067873b09a73908df488870c303bd9" - integrity sha512-I8cGRJj3pyOLs/HndoP+25vOqhqWkAZsWMEmq1qXy/b/M3ppufecUwaK2/TVDVxcV61/iSdhykUjQQ2DLSrTdg== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "*" - "@types/serve-static" "*" - -"@types/mime@*": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" - integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== - -"@types/mkdirp@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.0.tgz#16ce0eabe4a9a3afe64557ad0ee6886ec3d32927" - integrity sha512-ONFY9//bCEr3DWKON3iDv/Q8LXnhaYYaNDeFSN0AtO5o4sLf9F0pstJKKKjQhXE0kJEeHs8eR6SAsROhhc2Csw== - dependencies: - "@types/node" "*" - -"@types/node@*", "@types/node@^13.9.0": - version "13.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.0.tgz#5b6ee7a77faacddd7de719017d0bc12f52f81589" - integrity sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ== - -"@types/range-parser@*": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" - integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== - -"@types/serve-static@*": - version "1.13.3" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" - integrity sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g== - dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -ansi-align@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" - integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= - dependencies: - string-width "^2.0.0" - -ansi-regex@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" - integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -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" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -aws-sdk@^2.658.0: - version "2.814.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.814.0.tgz#7a1c36006e0b5826f14bd2511b1d229ef6814bb0" - integrity sha512-empd1m/J/MAkL6d9OeRpmg9thobULu0wk4v8W3JToaxGi2TD7PIdvE6yliZKyOVAdJINhBWEBhxR4OUIHhcGbQ== - dependencies: - buffer "4.9.2" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.15.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - uuid "3.3.2" - xml2js "0.4.19" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.0.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== - -binary-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" - integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== - -body-parser@1.19.2: - version "1.19.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" - integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.9.7" - raw-body "2.4.3" - type-is "~1.6.18" - -boxen@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" - integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== - dependencies: - ansi-align "^2.0.0" - camelcase "^4.0.0" - chalk "^2.0.1" - cli-boxes "^1.0.0" - string-width "^2.0.0" - term-size "^1.2.0" - widest-line "^2.0.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -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== - -buffer@4.9.2: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -camelcase@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= - -capture-stack-trace@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" - integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chokidar@^3.2.2: - version "3.3.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" - integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.3.0" - optionalDependencies: - fsevents "~2.1.2" - -ci-info@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" - integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== - -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -commander@^2.12.1: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -configstore@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" - integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw== - dependencies: - dot-prop "^4.1.0" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - -create-error-class@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" - integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= - dependencies: - capture-stack-trace "^1.0.0" - -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-random-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" - integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= - -debug@2.6.9, debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -dot-prop@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4" - integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ== - dependencies: - is-obj "^1.0.0" - -dotenv@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -events@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -express@^4.17.1: - version "4.17.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" - integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.19.2" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.4.2" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.9.7" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.17.2" - serve-static "1.14.2" - setprototypeof "1.2.0" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -glob-parent@~5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@^7.1.1: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" - integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= - dependencies: - ini "^1.3.4" - -got@^6.7.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" - integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= - dependencies: - create-error-class "^3.0.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-redirect "^1.0.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - lowercase-keys "^1.0.0" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - unzip-response "^2.0.1" - url-parse-lax "^1.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" - integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -http-errors@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" - integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.1" - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ieee754@1.1.13, ieee754@^1.1.4: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -ignore-by-default@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" - integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@^1.3.4, ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-ci@^1.0.10: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.2.1.tgz#e3779c8ee17fccf428488f6e281187f2e632841c" - integrity sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg== - dependencies: - ci-info "^1.5.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-installed-globally@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" - integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= - dependencies: - global-dirs "^0.1.0" - is-path-inside "^1.0.0" - -is-npm@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" - integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" - -is-redirect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" - integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= - -is-retry-allowed@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -isarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -jmespath@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" - integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -latest-version@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" - integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= - dependencies: - package-json "^4.0.0" - -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== - dependencies: - pify "^3.0.0" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== - dependencies: - mime-db "1.43.0" - -mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== - -mkdirp@^0.5.3: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -ms@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -nodemon@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.2.tgz#9c7efeaaf9b8259295a97e5d4585ba8f0cbe50b0" - integrity sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw== - dependencies: - chokidar "^3.2.2" - debug "^3.2.6" - ignore-by-default "^1.0.1" - minimatch "^3.0.4" - pstree.remy "^1.1.7" - semver "^5.7.1" - supports-color "^5.5.0" - touch "^3.1.0" - undefsafe "^2.0.2" - update-notifier "^2.5.0" - -nopt@~1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= - dependencies: - abbrev "1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -package-json@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" - integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= - dependencies: - got "^6.7.1" - registry-auth-token "^3.0.1" - registry-url "^3.0.3" - semver "^5.1.0" - -parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-parse@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path@^0.12.7: - version "0.12.7" - resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" - integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= - dependencies: - process "^0.11.1" - util "^0.10.3" - -picomatch@^2.0.4, picomatch@^2.0.7: - version "2.2.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" - integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - -prettier@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" - integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== - -process@^0.11.1: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - -pstree.remy@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3" - integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A== - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -qs@6.9.7: - version "6.9.7" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" - integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== - -query-string@^6.11.1: - version "6.11.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.11.1.tgz#ab021f275d463ce1b61e88f0ce6988b3e8fe7c2c" - integrity sha512-1ZvJOUl8ifkkBxu2ByVM/8GijMIPx+cef7u3yroO3Ogm4DOdZcF5dcrWTIlSHe3Pg/mtlt6/eFjObDfJureZZA== - dependencies: - decode-uri-component "^0.2.0" - split-on-first "^1.0.0" - strict-uri-encode "^2.0.0" - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" - integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== - dependencies: - bytes "3.1.2" - http-errors "1.8.1" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@^1.0.1, rc@^1.1.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -readdirp@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" - integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== - dependencies: - picomatch "^2.0.7" - -registry-auth-token@^3.0.1: - version "3.4.0" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e" - integrity sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A== - dependencies: - rc "^1.1.6" - safe-buffer "^5.0.1" - -registry-url@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" - integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= - dependencies: - rc "^1.0.1" - -resolve@^1.3.2: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== - dependencies: - path-parse "^1.0.6" - -safe-buffer@5.2.1, safe-buffer@^5.0.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" - integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= - -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver-diff@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" - integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= - dependencies: - semver "^5.0.3" - -semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -send@0.17.2: - version "0.17.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" - integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "1.8.1" - mime "1.6.0" - ms "2.1.3" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serve-static@1.14.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" - integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.2" - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= - -source-map-support@^0.5.6: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -split-on-first@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" - integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -"statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -strict-uri-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" - integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= - -string-width@^2.0.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -supports-color@^5.3.0, supports-color@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -term-size@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" - integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= - dependencies: - execa "^0.7.0" - -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -touch@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" - integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== - dependencies: - nopt "~1.0.10" - -ts-node@^8.6.2: - version "8.6.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35" - integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.6" - yn "3.1.1" - -tslib@^1.10.0, tslib@^1.8.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" - integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== - -tslint-config-prettier@^1.18.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" - integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== - -tslint@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.1.tgz#ac03fbd17f85bfefaae348b353b25a88efe10cde" - integrity sha512-kd6AQ/IgPRpLn6g5TozqzPdGNZ0q0jtXW4//hRcj10qLYBaa3mTUU2y2MCG+RXZm8Zx+KZi0eA+YCrMyNlF4UA== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.3" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.10.0" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typescript@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" - integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== - -undefsafe@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" - integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== - dependencies: - debug "^2.2.0" - -unique-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" - integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= - dependencies: - crypto-random-string "^1.0.0" - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unzip-response@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" - integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= - -update-notifier@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" - integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== - dependencies: - boxen "^1.2.1" - chalk "^2.0.1" - configstore "^3.0.0" - import-lazy "^2.1.0" - is-ci "^1.0.10" - is-installed-globally "^0.1.0" - is-npm "^1.0.0" - latest-version "^3.0.0" - semver-diff "^2.0.0" - xdg-basedir "^3.0.0" - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= - dependencies: - prepend-http "^1.0.1" - -url@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" - integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -widest-line@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" - integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== - dependencies: - string-width "^2.1.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write-file-atomic@^2.0.0: - version "2.4.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" - integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - -xdg-basedir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" - integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= - -xml2js@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== - dependencies: - sax ">=0.6.0" - xmlbuilder "~9.0.1" - -xmlbuilder@~9.0.1: - version "9.0.7" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" - integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==