From 1dc2e495bebad68d9a5ff2b210ec29ae32bbe229 Mon Sep 17 00:00:00 2001 From: develar Date: Wed, 11 Oct 2017 09:18:24 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20differential=20update=20=E2=80=94=20use?= =?UTF-8?q?=20content=20defined=20chunking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dictionaries/develar.xml | 4 + .../Debug_differential_update_buider.xml | 8 + .../Debug_differential_update_builder.xml | 8 + package.json | 7 +- packages/app-package-builder/package.json | 3 +- .../src/ContentDefinedChunker.ts | 86 +++++++++ packages/app-package-builder/src/blockMap.ts | 80 +++++--- packages/app-package-builder/src/main.ts | 3 +- .../builder-util-runtime/src/blockMapApi.ts | 16 +- packages/electron-builder/package.json | 2 +- .../electron-builder/src/targets/nsis/nsis.ts | 9 + packages/electron-publisher-s3/package.json | 2 +- packages/electron-updater/CHANGELOG.md | 6 + packages/electron-updater/package.json | 5 +- .../src/differentialPackage.ts | 75 ++++---- yarn.lock | 177 ++++++++++++------ 16 files changed, 355 insertions(+), 136 deletions(-) create mode 100644 .idea/runConfigurations/Debug_differential_update_buider.xml create mode 100644 .idea/runConfigurations/Debug_differential_update_builder.xml create mode 100644 packages/app-package-builder/src/ContentDefinedChunker.ts diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml index 457c173b9af..e534df15401 100644 --- a/.idea/dictionaries/develar.xml +++ b/.idea/dictionaries/develar.xml @@ -44,11 +44,14 @@ checksums chipset chown + chunker + chunking circleci clcerts cleanuped cleanuper codesigning + confirmative controlfields coroutine cpus @@ -349,6 +352,7 @@ zenity zisofs zlib + zsync \ No newline at end of file diff --git a/.idea/runConfigurations/Debug_differential_update_buider.xml b/.idea/runConfigurations/Debug_differential_update_buider.xml new file mode 100644 index 00000000000..91832dfd17e --- /dev/null +++ b/.idea/runConfigurations/Debug_differential_update_buider.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Debug_differential_update_builder.xml b/.idea/runConfigurations/Debug_differential_update_builder.xml new file mode 100644 index 00000000000..c4bc7763942 --- /dev/null +++ b/.idea/runConfigurations/Debug_differential_update_builder.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/package.json b/package.json index 5e96af0dd88..6b701c617a6 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "7zip-bin": "^2.2.4", "archiver": "^2.0.3", "async-exit-hook": "^2.0.1", - "aws-sdk": "^2.130.0", + "aws-sdk": "^2.131.0", "bluebird-lst": "^1.0.4", "chalk": "^2.1.0", "chromium-pickle-js": "^0.2.0", @@ -60,6 +60,7 @@ "normalize-package-data": "^2.4.0", "parse-color": "^1.0.0", "plist": "^2.1.0", + "rabin-bindings": "~1.7.3", "read-config-file": "1.1.1", "sanitize-filename": "^1.6.1", "semver": "^5.4.1", @@ -67,7 +68,7 @@ "stat-mode": "^0.2.2", "temp-file": "^2.0.3", "tunnel-agent": "^0.6.0", - "update-notifier": "^2.2.0", + "update-notifier": "^2.3.0", "xelement": "^1.0.16", "yargs": "^9.0.1" }, @@ -92,7 +93,7 @@ "babel-preset-ts-node4-bluebird": "^0.1.1", "convert-source-map": "^1.5.0", "decompress-zip": "^0.3.0", - "depcheck": "^0.6.7", + "depcheck": "^0.6.8", "develar-typescript-json-schema": "0.17.0", "electron-builder-tslint-config": "^1.0.4", "env-paths": "^1.0.0", diff --git a/packages/app-package-builder/package.json b/packages/app-package-builder/package.json index c339ff86a62..193b7768707 100644 --- a/packages/app-package-builder/package.json +++ b/packages/app-package-builder/package.json @@ -16,7 +16,8 @@ "int64-buffer": "^0.1.9", "builder-util-runtime": "^0.0.0-semantic-release", "builder-util": "^0.0.0-semantic-release", - "js-yaml": "^3.10.0" + "js-yaml": "^3.10.0", + "rabin-bindings": "~1.7.3" }, "types": "./out/main.d.ts" } diff --git a/packages/app-package-builder/src/ContentDefinedChunker.ts b/packages/app-package-builder/src/ContentDefinedChunker.ts new file mode 100644 index 00000000000..1355f892d99 --- /dev/null +++ b/packages/app-package-builder/src/ContentDefinedChunker.ts @@ -0,0 +1,86 @@ +import { createHash } from "crypto" +import { read } from "fs-extra-p" +import { FileChunks } from "builder-util-runtime/out/blockMapApi" +import { Rabin } from "rabin-bindings" + +export class ContentDefinedChunker { + async computeChunks(fd: number, start: number, end: number, name: string): Promise { + console.log(name) + + const fileSize = end - start + const buffer = Buffer.allocUnsafe(Math.min(4 * 1024 * 1024, fileSize)) + + const rabin = Rabin() + const avgBits = 12 + const min = 8 * 1024 + // see note in the nsis.ts about archive dict size + const max = 32 * 1024 + rabin.configure(avgBits, min, max) + + const checksums: Array = [] + const allSizes: Array = [] + + let tailBufferData: Buffer | null = null + let readOffset = start + while (true) { + const actualBufferSize = Math.min(end - readOffset, buffer.length) + await read(fd, buffer, 0, actualBufferSize, readOffset) + + const dataBuffer: Buffer = buffer.length === actualBufferSize ? buffer : buffer.slice(0, actualBufferSize) + const sizes: Array = [] + rabin.fingerprint([dataBuffer], sizes) + + let chunkStart = 0 + for (const size of sizes) { + allSizes.push(size) + let chunkEnd = chunkStart + size + + const hash = createHash("sha256") + if (tailBufferData !== null) { + hash.update(tailBufferData) + // if there is the tail data (already processed by rabin data), first size includes it + chunkEnd -= tailBufferData.length + tailBufferData = null + } + hash.update(dataBuffer.slice(chunkStart, chunkEnd)) + checksums.push(hash.digest("base64")) + chunkStart = chunkEnd + } + + const tailSize = actualBufferSize - chunkStart + if (tailSize !== 0) { + if (tailBufferData !== null) { + throw new Error(`Internal error (${name}): tailBufferData must be null`) + } + tailBufferData = dataBuffer.slice(chunkStart, chunkStart + tailSize) + } + + readOffset += actualBufferSize + if (readOffset >= end) { + if (tailBufferData !== null) { + allSizes.push(tailSize) + checksums.push(computeChecksum(tailBufferData)) + } + break + } + else if (tailBufferData !== null) { + // copy data + tailBufferData = Buffer.from(tailBufferData) + } + } + + const totalSize = allSizes.reduce((accumulator, currentValue) => accumulator + currentValue) + if (totalSize !== fileSize) { + throw new Error(`Internal error (${name}): size mismatch: expected: ${fileSize}, got: ${totalSize}`) + } + + return {checksums, sizes: allSizes} + } +} + +function computeChecksum(chunk: Buffer) { + // node-base91 doesn't make a lot of sense - 29KB vs 30KB Because for base64 string value in the yml never escaped, but node-base91 often escaped (single quotes) and it adds extra 2 symbols. + return createHash("sha256") + .update(chunk) + .digest("base64") +} diff --git a/packages/app-package-builder/src/blockMap.ts b/packages/app-package-builder/src/blockMap.ts index 729fcf641ef..c296b65a2fd 100644 --- a/packages/app-package-builder/src/blockMap.ts +++ b/packages/app-package-builder/src/blockMap.ts @@ -2,12 +2,12 @@ import BluebirdPromise from "bluebird-lst" import { hashFile } from "builder-util" import { PackageFileInfo } from "builder-util-runtime" import { BlockMap, SIGNATURE_HEADER_SIZE } from "builder-util-runtime/out/blockMapApi" -import { createHash } from "crypto" -import { appendFile, read, stat } from "fs-extra-p" +import { appendFile, stat, writeFile } from "fs-extra-p" import { safeDump } from "js-yaml" import { Archive } from "./Archive" import { SevenZArchiveEntry } from "./SevenZArchiveEntry" import { SevenZFile } from "./SevenZFile" +import { ContentDefinedChunker } from "./ContentDefinedChunker" const deflateRaw: any = BluebirdPromise.promisify(require("zlib").deflateRaw) @@ -27,6 +27,9 @@ export async function createDifferentialPackage(archiveFile: string): Promise> { - const chunkSize = 64 * 1024 - const buffer = Buffer.allocUnsafe(chunkSize) - const blocks = [] - - for (let offset = start; offset < end; offset += chunkSize) { - const actualChunkSize = Math.min(end - offset, chunkSize) - await read(fd, buffer, 0, actualChunkSize, offset) - - const hash = createHash("md5") - hash.update(actualChunkSize === chunkSize ? buffer : buffer.slice(0, actualChunkSize)) - // node-base91 doesn't make a lot of sense - 29KB vs 30KB Because for base64 string value in the yml never escaped, but node-base91 often escaped (single quotes) and it adds extra 2 symbols. - // And in any case data stored as deflated in the package. - blocks.push(hash.digest("base64")) - } - - return blocks -} - class BlockMapBuilder { private currentFolderIndex = -1 @@ -129,19 +113,61 @@ export async function computeBlockMap(sevenZFile: SevenZFile): Promise } } + const stats: Array = [] const blocks = await BluebirdPromise.map(files, async entry => { - const blocks = await computeBlocks(sevenZFile.fd, entry.dataStart, entry.dataEnd) + const chunker = new ContentDefinedChunker() + const blocks = await chunker.computeChunks(sevenZFile.fd, entry.dataStart, entry.dataEnd, entry.name) + + if (process.env.DEBUG_BLOCKMAP) { + stats.push(getStat(blocks.sizes, entry.name)) + } + return { name: entry.name.replace(/\\/g, "/"), offset: entry.dataStart, size: entry.dataEnd - entry.dataStart, - blocks, + ...blocks, + } + }, {concurrency: 2}) + + if (process.env.DEBUG_BLOCKMAP) { + let duplicate = 0 + let savedSize = 0 + // noinspection JSMismatchedCollectionQueryUpdate + const checksums: Array = [] + // noinspection JSMismatchedCollectionQueryUpdate + const sizes: Array = [] + const index = new Map() + for (const file of blocks) { + for (let i = 0; i < file.checksums.length; i++) { + const checksum = file.checksums[i] + const size = file.sizes[i] + if (index.has(checksum)) { + duplicate++ + savedSize += size + } + else { + index.set(checksum, checksums.length) + checksums.push(checksum) + sizes.push(size) + } + } } - }, {concurrency: 4}) + + console.log(stats.join("\n")) + console.log(`duplicates: ${duplicate}, saved: ${savedSize}`) + } + return { - blockSize: 64, - hashMethod: "md5", - compressionLevel: 9, + version: "2", files: blocks, } +} + +function getStat(sizes: Array, name: string) { + const sortedSizes = sizes.slice().sort((a, b) => a - b) + const middle = Math.floor(sortedSizes.length / 2) + const isEven = sortedSizes.length % 2 === 0 + const median = isEven ? (sortedSizes[middle] + sortedSizes[middle - 1]) / 2 : sortedSizes[middle] + return `${sizes.length} chunks generated for ${name} (min: ${sortedSizes[0]}, max: ${sortedSizes[sortedSizes.length - 1]}, median: ${median})` } \ No newline at end of file diff --git a/packages/app-package-builder/src/main.ts b/packages/app-package-builder/src/main.ts index daa9e6f4a9d..374a42ae494 100644 --- a/packages/app-package-builder/src/main.ts +++ b/packages/app-package-builder/src/main.ts @@ -7,8 +7,7 @@ if (process.mainModule === module) { require(a + "map-support").install() async function main() { - // const file = new SevenZFile("/Users/develar/Documents/onshape-desktop-shell/dist/Onshape-0.5.13-x64.nsis.7z") - const file = "/Users/develar/Documents/onshape-desktop-shell/dist/Onshape-0.5.13-x64.nsis.7z" + const file = "/Volumes/test/electron-builder-test/dist/nsis-web/TestApp-1.0.1-x64.nsis.7z" await createDifferentialPackage(file) // const archive = await file.read() // for (const entry of archive.files) { diff --git a/packages/builder-util-runtime/src/blockMapApi.ts b/packages/builder-util-runtime/src/blockMapApi.ts index 91a8c6438cb..91706005b43 100644 --- a/packages/builder-util-runtime/src/blockMapApi.ts +++ b/packages/builder-util-runtime/src/blockMapApi.ts @@ -1,20 +1,18 @@ export const BLOCK_MAP_FILE_NAME = "_blockMap.yml" export const SIGNATURE_HEADER_SIZE = 12 /* signature + 2 bytes version + 4 bytes CRC */ + 20 -export interface BlockMap { - blockSize: number - hashMethod: "sha256" | "md5" - - compressionLevel: 9 | 1 +export interface FileChunks { + checksums: Array + sizes: Array +} +export interface BlockMap { + version: "1" | "2" files: Array } -export interface BlockMapFile { +export interface BlockMapFile extends FileChunks { name: string offset: number size: number - - // size of block 64K, last block size `size % (64 * 1024)` - blocks: Array } \ No newline at end of file diff --git a/packages/electron-builder/package.json b/packages/electron-builder/package.json index 7fd91158cb4..e997906c91a 100644 --- a/packages/electron-builder/package.json +++ b/packages/electron-builder/package.json @@ -74,7 +74,7 @@ "plist": "^2.1.0", "sanitize-filename": "^1.6.1", "semver": "^5.4.1", - "update-notifier": "^2.2.0", + "update-notifier": "^2.3.0", "yargs": "^9.0.1", "debug": "^3.1.0", "asar-integrity": "0.0.0-semantic-release", diff --git a/packages/electron-builder/src/targets/nsis/nsis.ts b/packages/electron-builder/src/targets/nsis/nsis.ts index 67dbea460c2..dda2e55f084 100644 --- a/packages/electron-builder/src/targets/nsis/nsis.ts +++ b/packages/electron-builder/src/targets/nsis/nsis.ts @@ -72,6 +72,15 @@ export class NsisTarget extends Target { archiveOptions.solid = false // our reader doesn't support compressed headers archiveOptions.isArchiveHeaderCompressed = false + /* + * dict size 64 MB: Full: 33,744.88 KB, To download: 17,630.3 KB (52%) + * dict size 16 MB: Full: 33,936.84 KB, To download: 16,175.9 KB (48%) + * dict size 8 MB: Full: 34,187.59 KB, To download: 8,229.9 KB (24%) + * dict size 4 MB: Full: 34,628.73 KB, To download: 3,782.97 KB (11%) + + as we can see, if file changed in one place, all block is invalidated (and update size approximately equals to dict size) + */ + archiveOptions.dictSize = 8 // do not allow to change compression level to avoid different packages compression = "normal" } diff --git a/packages/electron-publisher-s3/package.json b/packages/electron-publisher-s3/package.json index 09ddbd90bd8..690547e59f8 100644 --- a/packages/electron-publisher-s3/package.json +++ b/packages/electron-publisher-s3/package.json @@ -12,7 +12,7 @@ ], "dependencies": { "fs-extra-p": "^4.4.3", - "aws-sdk": "^2.130.0", + "aws-sdk": "^2.131.0", "mime": "^2.0.3", "electron-publish": "~0.0.0-semantic-release", "builder-util": "^0.0.0-semantic-release", diff --git a/packages/electron-updater/CHANGELOG.md b/packages/electron-updater/CHANGELOG.md index 0c44aa76b22..2cac44c5c60 100644 --- a/packages/electron-updater/CHANGELOG.md +++ b/packages/electron-updater/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.11.0 + +### Features + +* Differential updater: use [content defined chunking](https://github.com/electron-userland/electron-builder/releases/tag/v19.36.0) + # 2.10.2 ### Bug Fixes diff --git a/packages/electron-updater/package.json b/packages/electron-updater/package.json index e0460b0ccef..04a5683e2dd 100644 --- a/packages/electron-updater/package.json +++ b/packages/electron-updater/package.json @@ -23,5 +23,8 @@ "xelement": "^1.0.16", "lodash.isequal": "^4.5.0" }, - "typings": "./out/main.d.ts" + "typings": "./out/main.d.ts", + "publishConfig": { + "tag": "next" + } } diff --git a/packages/electron-updater/src/differentialPackage.ts b/packages/electron-updater/src/differentialPackage.ts index d3b480ed3d9..192f1cb694c 100644 --- a/packages/electron-updater/src/differentialPackage.ts +++ b/packages/electron-updater/src/differentialPackage.ts @@ -17,6 +17,16 @@ export class DifferentialDownloaderOptions { readonly requestHeaders: OutgoingHttpHeaders | null } +function buildChecksumToOffsetMap(file: BlockMapFile, fileOffset: number) { + const result = new Map() + let offset = fileOffset + for (let i = 0; i < file.checksums.length; i++) { + result.set(file.checksums[i], offset) + offset += file.sizes[i] + } + return result +} + export class DifferentialDownloader { private readonly baseRequestOptions: RequestOptions @@ -49,11 +59,8 @@ export class DifferentialDownloader { const newBlockMap = await readBlockMap(this.fileMetadataBuffer.slice(this.packageInfo.headerSize!!)) // we don't check other metadata like compressionMethod - generic check that it is make sense to differentially update is suitable for it - if (oldBlockMap.hashMethod !== newBlockMap.hashMethod) { - throw new Error(`hashMethod is different (${oldBlockMap.hashMethod} - ${newBlockMap.hashMethod}), full download is required`) - } - if (oldBlockMap.blockSize !== newBlockMap.blockSize) { - throw new Error(`blockSize is different (${oldBlockMap.blockSize} - ${newBlockMap.blockSize}), full download is required`) + if (oldBlockMap.version !== newBlockMap.version) { + throw new Error(`version is different (${oldBlockMap.version} - ${newBlockMap.version}), full download is required`) } const operations = this.computeOperations(oldBlockMap, newBlockMap) @@ -78,7 +85,7 @@ export class DifferentialDownloader { throw new Error(`Internal error, size mismatch: downloadSize: ${downloadSize}, copySize: ${copySize}, newPackageSize: ${newPackageSize}`) } - this.logger.info(`Full: ${formatBytes(newPackageSize)}, To download: ${formatBytes(downloadSize)} (${Math.round((((newPackageSize - downloadSize) / newPackageSize) * 100))}%)`) + this.logger.info(`Full: ${formatBytes(newPackageSize)}, To download: ${formatBytes(downloadSize)} (${Math.round(downloadSize / (newPackageSize / 100))}%)`) await this.downloadFile(operations) } @@ -178,9 +185,6 @@ export class DifferentialDownloader { const nameToOldBlocks = buildBlockFileMap(oldBlockMap.files) const nameToNewBlocks = buildBlockFileMap(newBlockMap.files) - // convert kb to bytes - const blockSize = newBlockMap.blockSize * 1024 - const oldEntryMap = buildEntryMap(oldBlockMap.files) const operations: Array = [] @@ -205,33 +209,22 @@ export class DifferentialDownloader { let changedBlockCount = 0 + const checksumToOldOffset = buildChecksumToOffsetMap(oldFile, oldEntry.offset) + + let newOffset = 0 blockMapLoop: - for (let i = 0; i < newFile.blocks.length; i++) { - if (i >= oldFile.blocks.length) { - break - } + for (let i = 0; i < newFile.checksums.length; newOffset += newFile.sizes[i], i++) { + const currentBlockSize = newFile.sizes[i] - const isFirstBlock = i === 0 - const isLastBlock = i === (newFile.blocks.length - 1) - const currentBlockSize = isLastBlock ? (newFile.size % blockSize) : blockSize - - if (oldFile.blocks[i] === newFile.blocks[i]) { - if (lastOperation == null || lastOperation.kind !== OperationKind.COPY) { - const start = oldEntry.offset + (i * blockSize) - const end = start + currentBlockSize - if (isFirstBlock) { - if (operations.length > 0) { - const prevOperation = operations[operations.length - 1] - if (prevOperation.kind === OperationKind.COPY && prevOperation.end === start) { - lastOperation = prevOperation - prevOperation.end = end - continue blockMapLoop - } - } - } + const oldOffset = checksumToOldOffset.get(newFile.checksums[i]) + if (oldOffset == null) { + changedBlockCount++ + const start = blockMapFile.offset + newOffset + const end = start + currentBlockSize + if (lastOperation == null || lastOperation.kind !== OperationKind.DOWNLOAD) { lastOperation = { - kind: OperationKind.COPY, + kind: OperationKind.DOWNLOAD, start, end, } @@ -242,14 +235,20 @@ export class DifferentialDownloader { } } else { - changedBlockCount++ + if (lastOperation == null || lastOperation.kind !== OperationKind.COPY || lastOperation.end !== oldOffset) { + const end: number = oldOffset + currentBlockSize + if (i === 0 && operations.length > 0) { + const prevOperation = operations[operations.length - 1] + if (prevOperation.kind === OperationKind.COPY && prevOperation.end === oldOffset) { + lastOperation = prevOperation + prevOperation.end = end + continue blockMapLoop + } + } - const start = blockMapFile.offset + (i * blockSize) - const end = start + currentBlockSize - if (lastOperation == null || lastOperation.kind !== OperationKind.DOWNLOAD) { lastOperation = { - kind: OperationKind.DOWNLOAD, - start, + kind: OperationKind.COPY, + start: oldOffset, end, } operations.push(lastOperation) diff --git a/yarn.lock b/yarn.lock index 322b9a53fbb..22be3f45f17 100644 --- a/yarn.lock +++ b/yarn.lock @@ -453,9 +453,9 @@ asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" -aws-sdk@^2.130.0: - version "2.130.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.130.0.tgz#f0c6da7dbda9a15cb0c2beb3030f6da2a4c03c36" +aws-sdk@^2.131.0: + version "2.131.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.131.0.tgz#5b213fa493ce36a88130b50bef0e9a0cb6cd9f72" dependencies: buffer "4.9.1" crypto-browserify "1.0.9" @@ -765,6 +765,10 @@ binary@^0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" +bindings@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" + bl@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" @@ -826,7 +830,7 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boxen@^1.0.0: +boxen@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.2.1.tgz#0f11e7fe344edb9397977fc13ede7f64d956481d" dependencies: @@ -920,10 +924,6 @@ camelcase@^2.0.0, camelcase@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -955,7 +955,7 @@ chainsaw@~0.1.0: dependencies: traverse ">=0.3.0 <0.4" -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1021,6 +1021,10 @@ chokidar@^1.6.0, chokidar@^1.7.0: optionalDependencies: fsevents "^1.0.0" +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + chromium-pickle-js@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" @@ -1406,9 +1410,9 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depcheck@^0.6.7: - version "0.6.7" - resolved "https://registry.yarnpkg.com/depcheck/-/depcheck-0.6.7.tgz#6b3d1e993931e09ee673d3507d3175db734823e6" +depcheck@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/depcheck/-/depcheck-0.6.8.tgz#822bd72efe900afd66b78b59dd269ba63b10020c" dependencies: babel-traverse "^6.7.3" babylon "^6.1.21" @@ -1420,7 +1424,7 @@ depcheck@^0.6.7: minimatch "^3.0.2" require-package-name "^2.0.1" walkdir "0.0.11" - yargs "^6.0.0" + yargs "^8.0.2" depd@1.1.1, depd@~1.1.0, depd@~1.1.1: version "1.1.1" @@ -1609,7 +1613,7 @@ encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" -end-of-stream@^1.0.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" dependencies: @@ -1783,6 +1787,10 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" +expand-template@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-1.1.0.tgz#e09efba977bf98f9ee0ed25abd0c692e02aec3fc" + expect@^21.2.1: version "21.2.1" resolved "https://registry.yarnpkg.com/expect/-/expect-21.2.1.tgz#003ac2ac7005c3c29e73b38a272d4afadd6d1d7b" @@ -2117,6 +2125,10 @@ gitbook-plugin-theme-default@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/gitbook-plugin-theme-default/-/gitbook-plugin-theme-default-1.0.7.tgz#51f71fbca74a261054e9e9d141c42167afefce88" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + github-slugid@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/github-slugid/-/github-slugid-1.0.1.tgz#bccdd0815bfad69d8a359fa4fd65947d606ec3c0" @@ -2155,6 +2167,12 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, gl once "^1.3.0" path-is-absolute "^1.0.0" +global-dirs@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.0.tgz#10d34039e0df04272e262cf24224f7209434df4f" + dependencies: + ini "^1.3.4" + globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -2467,6 +2485,13 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" +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" + 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" @@ -2487,6 +2512,12 @@ is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + dependencies: + path-is-inside "^1.0.1" + is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -3424,7 +3455,7 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -nan@^2.3.0: +nan@^2.3.0, nan@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" @@ -3440,6 +3471,10 @@ next-tick@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" +node-abi@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.1.1.tgz#c9cda256ec8aa99bcab2f6446db38af143338b2a" + node-emoji@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.8.1.tgz#6eec6bfb07421e2148c75c6bba72421f8530a826" @@ -3486,6 +3521,10 @@ node.flow@1.2.3: dependencies: node.extend "1.0.8" +noop-logger@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" + nopt@^3.0.1: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" @@ -3526,7 +3565,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npmlog@^4.0.2: +npmlog@^4.0.1, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -3617,7 +3656,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.3.3, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -3655,7 +3694,7 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -3758,6 +3797,10 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" +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" + path-key@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -3814,6 +3857,25 @@ plist@^2.1.0: xmlbuilder "8.2.2" xmldom "0.1.x" +prebuild-install@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-2.3.0.tgz#19481247df728b854ab57b187ce234211311b485" + dependencies: + expand-template "^1.0.2" + github-from-package "0.0.0" + minimist "^1.2.0" + mkdirp "^0.5.1" + node-abi "^2.1.1" + noop-logger "^0.1.1" + npmlog "^4.0.1" + os-homedir "^1.0.1" + pump "^1.0.1" + rc "^1.1.6" + simple-get "^1.4.2" + tar-fs "^1.13.0" + tunnel-agent "^0.6.0" + xtend "4.0.1" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -3863,6 +3925,13 @@ pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" +pump@^1.0.0, pump@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.2.tgz#3b3ee6512f94f0e575538c17995f9f16990a5d51" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -3891,6 +3960,14 @@ querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" +rabin-bindings@~1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/rabin-bindings/-/rabin-bindings-1.7.3.tgz#fb6ae9dbf897988bc2504ccf4832ee4f0546d32a" + dependencies: + bindings "^1.3.0" + nan "^2.7.0" + prebuild-install "^2.3.0" + randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -4321,6 +4398,14 @@ 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" +simple-get@^1.4.2: + version "1.4.3" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-1.4.3.tgz#e9755eda407e96da40c5e5158c9ea37b33becbeb" + dependencies: + once "^1.3.1" + unzip-response "^1.0.0" + xtend "^4.0.0" + single-line-log@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-1.1.2.tgz#c2f83f273a3e1a16edb0995661da0ed5ef033364" @@ -4586,6 +4671,15 @@ taffydb@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" +tar-fs@^1.13.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.0.tgz#e877a25acbcc51d8c790da1c57c9cf439817b896" + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + tar-pack@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" @@ -4599,7 +4693,7 @@ tar-pack@^3.4.0: tar "^2.2.1" uid-number "^0.0.6" -tar-stream@^1.5.0: +tar-stream@^1.1.2, tar-stream@^1.5.0: version "1.5.4" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016" dependencies: @@ -4888,18 +4982,23 @@ unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" +unzip-response@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe" + unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" -update-notifier@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.2.0.tgz#1b5837cf90c0736d88627732b661c138f86de72f" +update-notifier@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" dependencies: - boxen "^1.0.0" - chalk "^1.0.0" + boxen "^1.2.1" + chalk "^2.0.1" configstore "^3.0.0" import-lazy "^2.1.0" + is-installed-globally "^0.1.0" is-npm "^1.0.0" latest-version "^3.0.0" semver-diff "^2.0.0" @@ -5045,10 +5144,6 @@ whatwg-url@^4.3.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -5186,7 +5281,7 @@ xmlhttprequest@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.7.0.tgz#dc697a8df0258afacad526c1c296b1bdd12c4ab3" -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: +xtend@4.0.1, "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -5204,12 +5299,6 @@ yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" -yargs-parser@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" - dependencies: - camelcase "^3.0.0" - yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" @@ -5228,24 +5317,6 @@ yargs@^3.32.0: window-size "^0.1.4" y18n "^3.2.0" -yargs@^6.0.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^4.2.0" - yargs@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360"