From 022502c772281da22c9477e9bde5a7fe7dea3e99 Mon Sep 17 00:00:00 2001 From: develar Date: Sat, 13 Feb 2016 18:18:35 +0100 Subject: [PATCH] Merge electron-complete-builder project into electron-builder --- .gitignore | 17 +- .idea/codeStyleSettings.xml | 82 + .idea/dictionaries/develar.xml | 36 + .idea/electron-builder.iml | 15 + .idea/inspectionProfiles/Project_Default.xml | 10 + .../inspectionProfiles/profiles_settings.xml | 7 + .idea/jsLibraryMappings.xml | 8 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .../ArtifactPublisherTest.xml | 5 + .idea/runConfigurations/BuildTest.xml | 5 + .idea/runConfigurations/RepoSlugTest.xml | 5 + .idea/scopes/src.xml | 3 + .idea/typescript-compiler.xml | 10 + .idea/vcs.xml | 6 + .idea/watcherTasks.xml | 4 + .npmignore | 8 + .travis.yml | 46 +- appveyor.yml | 23 + lib/linux.d.ts | 5 + package.json | 71 +- src/awaiter.ts | 5 + src/build-cli.ts | 41 + src/builder.ts | 98 + src/codeSign.ts | 100 + src/gitHubPublisher.ts | 150 ++ src/gitHubRequest.ts | 90 + src/httpRequest.ts | 69 + src/install-app-deps.ts | 19 + src/linuxPackager.ts | 57 + src/macPackager.ts | 110 + src/packager.ts | 193 ++ src/platformPackager.ts | 108 + src/promise.ts | 61 + src/promisifed-fs.ts | 59 + src/repositoryInfo.ts | 106 + src/util.ts | 94 + src/winPackager.ts | 123 + test/ArtifactPublisherTest.js | 60 + test/BuildTest.js | 125 + test/CodeSignTest.js | 23 + test/README.md | 13 + test/RepoSlugTest.js | 53 + .../test-app-one/build/background.png | Bin 0 -> 9039 bytes test/fixtures/test-app-one/build/icon.icns | Bin 0 -> 82844 bytes test/fixtures/test-app-one/build/icon.ico | Bin 0 -> 370070 bytes test/fixtures/test-app-one/index.html | 13 + test/fixtures/test-app-one/index.js | 51 + test/fixtures/test-app-one/package.json | 22 + test/fixtures/test-app/app/index.html | 13 + test/fixtures/test-app/app/index.js | 51 + test/fixtures/test-app/app/package.json | 16 + test/fixtures/test-app/build/background.png | Bin 0 -> 9039 bytes test/fixtures/test-app/build/icon.icns | Bin 0 -> 82844 bytes test/fixtures/test-app/build/icon.ico | Bin 0 -> 370070 bytes test/fixtures/test-app/package.json | 9 + test/helpers/codeSignData.js | 3 + tsconfig.json | 61 + tslint.json | 66 + typings.json | 12 + typings/appdmg.d.ts | 24 + typings/bluebird.d.ts | 63 + typings/command-line-args.d.ts | 15 + typings/electron-packager.d.ts | 115 + typings/gh-api.d.ts | 15 + typings/hosted-git-info.d.ts | 10 + typings/json-parse-helpfulerror.d.ts | 3 + typings/main/ambient/fs-extra/fs-extra.d.ts | 206 ++ typings/main/ambient/mime/mime.d.ts | 22 + typings/main/ambient/progress/progress.d.ts | 122 + typings/main/ambient/should/should.d.ts | 172 ++ .../source-map-support.d.ts | 46 + typings/node.d.ts | 2179 +++++++++++++++++ typings/progress-stream.d.ts | 16 + typings/rimraf.d.ts | 16 + 75 files changed, 5446 insertions(+), 32 deletions(-) create mode 100644 .idea/codeStyleSettings.xml create mode 100644 .idea/dictionaries/develar.xml create mode 100644 .idea/electron-builder.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/jsLibraryMappings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/ArtifactPublisherTest.xml create mode 100644 .idea/runConfigurations/BuildTest.xml create mode 100644 .idea/runConfigurations/RepoSlugTest.xml create mode 100644 .idea/scopes/src.xml create mode 100644 .idea/typescript-compiler.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/watcherTasks.xml create mode 100644 appveyor.yml create mode 100644 lib/linux.d.ts create mode 100644 src/awaiter.ts create mode 100644 src/build-cli.ts create mode 100644 src/builder.ts create mode 100644 src/codeSign.ts create mode 100644 src/gitHubPublisher.ts create mode 100644 src/gitHubRequest.ts create mode 100644 src/httpRequest.ts create mode 100644 src/install-app-deps.ts create mode 100644 src/linuxPackager.ts create mode 100644 src/macPackager.ts create mode 100644 src/packager.ts create mode 100644 src/platformPackager.ts create mode 100644 src/promise.ts create mode 100644 src/promisifed-fs.ts create mode 100644 src/repositoryInfo.ts create mode 100644 src/util.ts create mode 100644 src/winPackager.ts create mode 100644 test/ArtifactPublisherTest.js create mode 100644 test/BuildTest.js create mode 100644 test/CodeSignTest.js create mode 100644 test/README.md create mode 100644 test/RepoSlugTest.js create mode 100644 test/fixtures/test-app-one/build/background.png create mode 100644 test/fixtures/test-app-one/build/icon.icns create mode 100644 test/fixtures/test-app-one/build/icon.ico create mode 100644 test/fixtures/test-app-one/index.html create mode 100644 test/fixtures/test-app-one/index.js create mode 100644 test/fixtures/test-app-one/package.json create mode 100644 test/fixtures/test-app/app/index.html create mode 100644 test/fixtures/test-app/app/index.js create mode 100644 test/fixtures/test-app/app/package.json create mode 100644 test/fixtures/test-app/build/background.png create mode 100644 test/fixtures/test-app/build/icon.icns create mode 100644 test/fixtures/test-app/build/icon.ico create mode 100644 test/fixtures/test-app/package.json create mode 100644 test/helpers/codeSignData.js create mode 100644 tsconfig.json create mode 100644 tslint.json create mode 100644 typings.json create mode 100644 typings/appdmg.d.ts create mode 100644 typings/bluebird.d.ts create mode 100644 typings/command-line-args.d.ts create mode 100644 typings/electron-packager.d.ts create mode 100644 typings/gh-api.d.ts create mode 100644 typings/hosted-git-info.d.ts create mode 100644 typings/json-parse-helpfulerror.d.ts create mode 100644 typings/main/ambient/fs-extra/fs-extra.d.ts create mode 100644 typings/main/ambient/mime/mime.d.ts create mode 100644 typings/main/ambient/progress/progress.d.ts create mode 100644 typings/main/ambient/should/should.d.ts create mode 100644 typings/main/definitions/source-map-support/source-map-support.d.ts create mode 100644 typings/node.d.ts create mode 100644 typings/progress-stream.d.ts create mode 100644 typings/rimraf.d.ts diff --git a/.gitignore b/.gitignore index a7ee1a4120e..4a40939de8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ -node_modules -tmp -*.log \ No newline at end of file +node_modules/ +tmp/ +*.log +out/ +npm-debug.log +dist/ +.idea/compiler.xml +.idea/encodings.xml +.idea/copyright/profiles_settings.xml +.idea/workspace.xml +typings/browser/ +typings/browser.d.ts +typings/main.d.ts +.DS_Store \ No newline at end of file diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml new file mode 100644 index 00000000000..f0ceae48247 --- /dev/null +++ b/.idea/codeStyleSettings.xml @@ -0,0 +1,82 @@ + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/develar.xml b/.idea/dictionaries/develar.xml new file mode 100644 index 00000000000..baff5ceaa99 --- /dev/null +++ b/.idea/dictionaries/develar.xml @@ -0,0 +1,36 @@ + + + + actperepo + appveyor + archs + authenticode + awaiter + circleci + clcerts + coroutine + crypto + debian + disturl + dpkg + enoent + github + globaldots + libgcrypt + makedeb + nokeys + nomacver + nupkg + nuspec + passin + pkcs + postinstall + promisify + repos + rimraf + tsconfig + veyor + winstaller + + + \ No newline at end of file diff --git a/.idea/electron-builder.iml b/.idea/electron-builder.iml new file mode 100644 index 00000000000..68a8f5ac7af --- /dev/null +++ b/.idea/electron-builder.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000000..b3f3a92318f --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000000..3b312839bf2 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 00000000000..7d4350ea56f --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000000..28a804d8932 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000000..cba49da8325 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/ArtifactPublisherTest.xml b/.idea/runConfigurations/ArtifactPublisherTest.xml new file mode 100644 index 00000000000..5ab2b39b2ee --- /dev/null +++ b/.idea/runConfigurations/ArtifactPublisherTest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/BuildTest.xml b/.idea/runConfigurations/BuildTest.xml new file mode 100644 index 00000000000..23a246fa719 --- /dev/null +++ b/.idea/runConfigurations/BuildTest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/RepoSlugTest.xml b/.idea/runConfigurations/RepoSlugTest.xml new file mode 100644 index 00000000000..df1116e0f04 --- /dev/null +++ b/.idea/runConfigurations/RepoSlugTest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/.idea/scopes/src.xml b/.idea/scopes/src.xml new file mode 100644 index 00000000000..c5fc9800750 --- /dev/null +++ b/.idea/scopes/src.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/typescript-compiler.xml b/.idea/typescript-compiler.xml new file mode 100644 index 00000000000..d10f84f5465 --- /dev/null +++ b/.idea/typescript-compiler.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000000..94a25f7f4cb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml new file mode 100644 index 00000000000..9338ba685e0 --- /dev/null +++ b/.idea/watcherTasks.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.npmignore b/.npmignore index 0d864fcd52b..d21a1ff3671 100644 --- a/.npmignore +++ b/.npmignore @@ -1,2 +1,10 @@ example-app *.spec.js +.idea/ +typings/ +src/ +tsconfig.json +tsd.json +npm-debug.log +test/ +docs/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 8a00753d7b2..9ac3468547d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,37 @@ -language: node_js -node_js: - - "0.12" - - "4" - - "5" - -branches: - only: - - master +os: + - osx + - linux + +env: + - NODE_VERSION=4 + - NODE_VERSION=5 + +language: ruby + +rvm: + - 2.2.0 + +cache: + directories: + - node_modules + - test/testApp/node_modules + - $HOME/.electron + - $HOME/.npm + +before_install: + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gnu-tar dpkg ; fi + - gem install fpm + +install: +- rm -rf ~/.nvm +- git clone https://github.com/creationix/nvm.git ~/.nvm +- source ~/.nvm/nvm.sh +- nvm install $NODE_VERSION +- npm install npm -g +- npm -v +- npm prune +- npm install + +script: +- npm run test \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000000..3e68c3713af --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,23 @@ +platform: + - x64 + +cache: + - node_modules + - '%APPDATA%\npm-cache' + - '%USERPROFILE%\.electron' + +init: + - git config --global core.autocrlf input + +install: + - ps: Install-Product node 5 x64 + - npm install npm -g + - node -v + - npm -v + - npm prune + - npm install + +build: off + +test_script: + - npm run test-win \ No newline at end of file diff --git a/lib/linux.d.ts b/lib/linux.d.ts new file mode 100644 index 00000000000..741c0266955 --- /dev/null +++ b/lib/linux.d.ts @@ -0,0 +1,5 @@ +declare class Linux { + build(options: any, callback: (error: Error, path: string) => void): void +} + +export function init(): Linux diff --git a/package.json b/package.json index b1a40d2d432..674d3b00e6b 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,24 @@ { "name": "electron-builder", "version": "2.7.2", - "description": "Create electron apps installers the easy way", + "description": "Complete solution to build ready for distribution and 'auto update' Electron App installers", "main": "index.js", "scripts": { - "lint": "eslint ./lib cli.js index.js", - "test": "npm run lint || exit 1; result=\"$(tape index.spec.js ./lib/*.spec.js ./lib/**/*.spec.js)\"; echo \"$result\" | tap-spec; echo \"$result\" | tnyan;", + "compile": "tsconfig -i 2 && ts-babel", + "lint": "eslint ./lib cli.js index.js && tslint src/*", + "pretest": "npm run compile && npm run lint", + "test": "result=\"$(tape index.spec.js ./lib/*.spec.js ./lib/**/*.spec.js)\"; echo \"$result\" | tap-spec; echo \"$result\" | tnyan; ava", + "test-win": "npm run pretest && ava", "test-build": "result=\"$(tape cli.spec.js)\"; echo \"$result\" | tap-spec; echo \"$result\" | tnyan;", - "publish-please": "publish-please" + "publish-please": "publish-please", + "release": "npm prune && npm test && npm run publish-please" }, "repository": { "type": "git", "url": "https://github.com/loopline-systems/electron-builder.git" }, "engines": { - "node": ">=0.12.0" + "node": ">=0.4.0" }, "keywords": [ "electron", @@ -36,33 +40,60 @@ "Mac" ], "bin": { - "electron-builder": "cli.js" + "electron-builder": "cli.js", + "install-app-deps": "./out/install-app-deps.js", + "build": "./out/build-cli.js" }, "author": "Stefan Judis", "license": "MIT", - "bugs": { - "url": "https://github.com/loopline-systems/electron-builder/issues" - }, + "bugs": "https://github.com/loopline-systems/electron-builder/issues", "homepage": "https://github.com/loopline-systems/electron-builder", "dependencies": { - "fs-extra": "^0.26.0", - "lodash.assign": "^4.0.1", - "lodash.camelcase": "^3.0.1", - "lodash.template": "^4.0.0", - "meow": "^3.1.0", - "mkdirp": "^0.5.1" + "bluebird": "^3.3.1", + "command-line-args": "^2.1.4", + "electron-packager-tf": "^5.2.1-beta.2", + "electron-winstaller-temp-fork": "^0.1.0", + "fs-extra": "^0.26.5", + "hosted-git-info": "^2.1.4", + "json-parse-helpfulerror": "^1.0.3", + "lodash.assign": "^4.0.2", + "lodash.camelcase": "^4.0.1", + "lodash.template": "^4.1.0", + "meow": "^3.7.0", + "mime": "^1.3.4", + "mkdirp": "^0.5.1", + "progress": "^1.1.8", + "progress-stream": "^1.2.0", + "rimraf": "^2.5.1", + "source-map-support": "^0.4.0" }, "optionalDependencies": { - "appdmg": "^0.3.1" + "appdmg": "^0.3.6" }, "devDependencies": { + "ava-tf": "^0.11.2-beta.0", + "babel-plugin-transform-es2015-parameters": "^6.5.0", "eslint": "^2.0.0", - "proxyquire": "^1.5.0", + "plist": "^1.2.0", + "proxyquire": "^1.7.4", "publish-please": "^1.1.0", - "rimraf": "^2.5.0", + "rimraf": "^2.5.2", + "should": "^8.2.2", "tap-nyan": "0.0.2", "tap-spec": "^4.1.1", - "tape": "^4.0.0", - "tmp": "0.0.28" + "tape": "^4.4.0", + "tmp": "0.0.28", + "ts-babel": "^0.3.0", + "tsconfig-glob": "^0.4.1", + "tslint": "^3.3.0", + "typescript": "^1.8.0-beta" + }, + "babel": { + "plugins": [ + "transform-es2015-parameters" + ] + }, + "ava": { + "verbose": true } } diff --git a/src/awaiter.ts b/src/awaiter.ts new file mode 100644 index 00000000000..070561e4a1f --- /dev/null +++ b/src/awaiter.ts @@ -0,0 +1,5 @@ +import { Promise as BluebirdPromise } from "bluebird" + +export function tsAwaiter(thisArg: any, _arguments: any, ignored: any, generator: Function) { + return BluebirdPromise.coroutine(generator).call(thisArg, _arguments) +} diff --git a/src/build-cli.ts b/src/build-cli.ts new file mode 100644 index 00000000000..df4b5883fce --- /dev/null +++ b/src/build-cli.ts @@ -0,0 +1,41 @@ +#! /usr/bin/env node + +import { PackagerOptions } from "./platformPackager" +import { build } from "./builder" +import { PublishOptions } from "./gitHubPublisher" +import { commonArgs } from "./util" +import { printErrorAndExit } from "./promise" +import { tsAwaiter } from "./awaiter" +import cla = require("command-line-args") + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +interface CliOptions extends PackagerOptions, PublishOptions { + build: boolean + help: boolean +} + +const cli = cla(commonArgs.concat( + {name: "arch", type: String, description: "ia32, x64 or all (by default)."}, + {name: "dist", type: Boolean, alias: "d", description: "Whether to package in a distributable format (e.g. DMG, windows installer, NuGet package)."}, + {name: "publish", type: String, alias: "p", description: "Publish artifacts (to GitHub Releases): onTag (on tag push only) or onTagOrDraft (on tag push or if draft release exists)."}, + {name: "build", type: Boolean, description: "Deprecated, use dist instead."}, + {name: "sign", type: String}, + {name: "platform", type: String, description: "darwin or win32. Current platform (" + process.platform + ") by default."}, + {name: "help", alias: "h", type: Boolean} +)) + +const args: CliOptions = cli.parse() + +if (args.help) { + console.log(cli.getUsage({hide: ["build"]})) +} +else { + if (args.build) { + args.dist = true + } + + build(args) + .catch(printErrorAndExit) +} \ No newline at end of file diff --git a/src/builder.ts b/src/builder.ts new file mode 100644 index 00000000000..b4961c25e22 --- /dev/null +++ b/src/builder.ts @@ -0,0 +1,98 @@ +import { Packager } from "./packager" +import { PackagerOptions } from "./platformPackager" +import { PublishOptions, Publisher, GitHubPublisher } from "./gitHubPublisher" +import { executeFinally } from "./promise" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" +import { InfoRetriever } from "./repositoryInfo" +import { log } from "./util" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export async function createPublisher(packager: Packager, options: BuildOptions, repoSlug: InfoRetriever, isPublishOptionGuessed: boolean = false): Promise { + const info = await repoSlug.getInfo(packager) + if (info == null) { + if (isPublishOptionGuessed) { + return null + } + + log("Cannot detect repository by .git/config") + throw new Error("Please specify 'repository' in the dev package.json ('" + packager.devPackageFile + "')") + } + else { + return new GitHubPublisher(info.user, info.project, packager.metadata.version, options.githubToken, options.publish !== "onTagOrDraft") + } +} + +export interface BuildOptions extends PackagerOptions, PublishOptions { +} + +export function build(options: BuildOptions = {}): Promise { + if (options.cscLink == null) { + options.cscLink = process.env.CSC_LINK + } + if (options.cscKeyPassword == null) { + options.cscKeyPassword = process.env.CSC_KEY_PASSWORD + } + + if (options.githubToken == null) { + options.githubToken = process.env.GH_TOKEN || process.env.GH_TEST_TOKEN + } + + const lifecycleEvent = process.env.npm_lifecycle_event + if (options.dist === undefined) { + options.dist = lifecycleEvent === "dist" || lifecycleEvent === "build" + } + + if (options.publish) { + options.dist = true + } + + let isPublishOptionGuessed = false + if (options.publish === undefined) { + if (lifecycleEvent === "release") { + options.publish = "always" + } + else { + const tag = process.env.TRAVIS_TAG || process.env.APPVEYOR_REPO_TAG_NAME || process.env.CIRCLE_TAG + if (tag != null && tag.length !== 0) { + log("Tag %s is defined, so artifacts will be published", tag) + options.publish = "onTag" + isPublishOptionGuessed = true + } + else if ((process.env.TRAVIS || process.env.APPVEYOR || process.env.CIRCLECI || "").toLowerCase() === "true") { + log("CI detected, so artifacts will be published if draft release exists") + options.publish = "onTagOrDraft" + isPublishOptionGuessed = true + } + } + } + + const publishTasks: Array> = [] + const repositoryInfo = new InfoRetriever() + const packager = new Packager(options, repositoryInfo) + if (options.publish != null && options.publish !== "never") { + let publisher: BluebirdPromise = null + packager.artifactCreated(path => { + if (publisher == null) { + publisher = >createPublisher(packager, options, repositoryInfo, isPublishOptionGuessed) + } + + if (publisher != null) { + publisher.then(it => publishTasks.push(>it.upload(path))) + } + }) + } + return executeFinally(packager.build(), error => { + if (error == null) { + return Promise.all(publishTasks) + } + else { + for (let task of publishTasks) { + task.cancel() + } + return null + } + }) +} \ No newline at end of file diff --git a/src/codeSign.ts b/src/codeSign.ts new file mode 100644 index 00000000000..bbb8d2c84be --- /dev/null +++ b/src/codeSign.ts @@ -0,0 +1,100 @@ +import { exec } from "./util" +import { deleteFile } from "./promisifed-fs" +import { download } from "./httpRequest" +import { tmpdir } from "os" +import * as path from "path" +import { executeFinally, all } from "./promise" +import { Promise as BluebirdPromise } from "bluebird" +import { randomBytes } from "crypto" +import { tsAwaiter } from "./awaiter" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export interface CodeSigningInfo { + cscName: string + cscKeychainName?: string +} + +function randomString(): string { + return randomBytes(8).toString("hex") +} + +export function generateKeychainName(): string { + return "csc-" + randomString() + ".keychain" +} + +export function createKeychain(keychainName: string, cscLink: string, cscKeyPassword: string): Promise { + const appleCertPath = path.join(tmpdir(), randomString() + ".cer") + const developerCertPath = path.join(tmpdir(), randomString() + ".p12") + + const keychainPassword = randomString() + return executeFinally(Promise.all([ + download("https://developer.apple.com/certificationauthority/AppleWWDRCA.cer", appleCertPath), + download(cscLink, developerCertPath), + BluebirdPromise.mapSeries([ + ["create-keychain", "-p", keychainPassword, keychainName], + ["unlock-keychain", "-p", keychainPassword, keychainName], + ["set-keychain-settings", "-t", "3600", "-u", keychainName] + ], it => exec("security", it)) + ]) + .then(() => importCerts(keychainName, appleCertPath, developerCertPath, cscKeyPassword)), + error => { + const tasks = [deleteFile(appleCertPath, true), deleteFile(developerCertPath, true)] + if (error != null) { + tasks.push(deleteKeychain(keychainName)) + } + return all(tasks) + }) +} + +async function importCerts(keychainName: string, appleCertPath: string, developerCertPath: string, cscKeyPassword: string): Promise { + await exec("security", ["import", appleCertPath, "-k", keychainName, "-T", "/usr/bin/codesign"]) + await exec("security", ["import", developerCertPath, "-k", keychainName, "-T", "/usr/bin/codesign", "-P", cscKeyPassword]) + let cscName = await extractCommonName(cscKeyPassword, developerCertPath) + return { + cscName: cscName, + cscKeychainName: keychainName + } +} + +function extractCommonName(password: string, certPath: string): BluebirdPromise { + return exec("openssl", ["pkcs12", "-nokeys", "-nodes", "-passin", "pass:" + password, "-nomacver", "-clcerts", "-in", certPath]) + .then(result => { + const match = result[0].toString().match(/^subject.*\/CN=([^\/]+)/m) + if (match == null || match[1] == null) { + throw new Error("Cannot extract common name from p12") + } + else { + return match[1] + } + }) +} + +export function sign(path: string, options: CodeSigningInfo): BluebirdPromise { + const args = ["--deep", "--force", "--sign", options.cscName, path] + if (options.cscKeychainName != null) { + args.push("--keychain", options.cscKeychainName) + } + return exec("codesign", args) +} + +export function deleteKeychain(keychainName: string, ignoreNotFound: boolean = true): BluebirdPromise { + const result = exec("security", ["delete-keychain", keychainName]) + if (ignoreNotFound) { + return result.catch(error => { + if (!error.message.includes("The specified keychain could not be found.")) { + throw error + } + }) + } + else { + return result + } +} + +export function downloadCertificate(cscLink: string): Promise { + const certPath = path.join(tmpdir(), randomString() + ".p12") + return download(cscLink, certPath) + .thenReturn(certPath) +} \ No newline at end of file diff --git a/src/gitHubPublisher.ts b/src/gitHubPublisher.ts new file mode 100644 index 00000000000..e41fc0a47ea --- /dev/null +++ b/src/gitHubPublisher.ts @@ -0,0 +1,150 @@ +import { Release, Asset } from "gh-release" +import { log } from "./util" +import { basename } from "path" +import { parse as parseUrl } from "url" +import * as mime from "mime" +import { stat } from "./promisifed-fs" +import { createReadStream } from "fs" +import { gitHubRequest, HttpError, doGitHubRequest } from "./gitHubRequest" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" +import { ReadStream } from "tty" +import progressStream = require("progress-stream") +import ProgressBar = require("progress") + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export interface Publisher { + upload(path: string): Promise +} + +export interface PublishOptions { + publish?: "onTag" | "onTagOrDraft" | "always" | "never" + githubToken?: string +} + +export class GitHubPublisher implements Publisher { + private tag: string + private _releasePromise: BluebirdPromise + + get releasePromise(): Promise { + return this._releasePromise + } + + constructor(private owner: string, private repo: string, version: string, private token: string, private createReleaseIfNotExists: boolean = true) { + if (token == null || token.length === 0) { + throw new Error("GitHub Personal Access Token is not specified") + } + + this.tag = "v" + version + this._releasePromise = >this.init() + } + + private async init(): Promise { + // we don't use "Get a release by tag name" because "tag name" means existing git tag, but we draft release and don't create git tag + let releases = await gitHubRequest>(`/repos/${this.owner}/${this.repo}/releases`, this.token) + for (let release of releases) { + if (release.tag_name === this.tag) { + if (!release.draft) { + if (this.createReleaseIfNotExists) { + throw new Error("Release must be a draft") + } + else { + return null + } + } + return release + } + } + + if (this.createReleaseIfNotExists) { + log("Release %s doesn't exists, creating one", this.tag) + return this.createRelease() + } + else { + return null + } + } + + async upload(path: string): Promise { + const fileName = basename(path) + const release = await this.releasePromise + if (release == null) { + return null + } + + const parsedUrl = parseUrl(release.upload_url.substring(0, release.upload_url.indexOf("{")) + "?name=" + fileName) + const fileStat = await stat(path) + uploadAttempt: for (let i = 0; i < 3; i++) { + const progressBar = (process.stdin).isTTY ? new ProgressBar(`Uploading ${fileName} [:bar] :percent :etas`, { + total: fileStat.size, + incomplete: " ", + stream: process.stdout, + width: 20, + }) : null + + try { + return await doGitHubRequest({ + hostname: parsedUrl.hostname, + path: parsedUrl.path, + method: "POST", + headers: { + Accept: "application/vnd.github.v3+json", + "User-Agent": "electron-complete-builder", + "Content-Type": mime.lookup(fileName), + "Content-Length": fileStat.size + } + }, this.token, (request, reject) => { + const fileInputStream = createReadStream(path) + fileInputStream.on("error", reject) + fileInputStream + .pipe(progressStream({ + length: fileStat.size, + time: 1000 + }, progress => progressBar == null ? console.log(".") : progressBar.tick(progress.delta))) + .pipe(request) + }) + } + catch (e) { + if (e instanceof HttpError) { + const httpError = e + if (httpError.response.statusCode === 422 && httpError.description != null && httpError.description.errors != null && httpError.description.errors[0].code === "already_exists") { + // delete old artifact and re-upload + log("Artifact %s already exists, overwrite one", fileName) + const assets = await gitHubRequest>(`/repos/${this.owner}/${this.repo}/releases/${release.id}/assets`, this.token) + for (let asset of assets) { + if (asset.name === fileName) { + await gitHubRequest(`/repos/${this.owner}/${this.repo}/releases/assets/${asset.id}`, this.token, null, "DELETE") + continue uploadAttempt + } + } + + log("Artifact %s not found, trying to upload again", fileName) + continue + } + } + + throw e + } + } + } + + private createRelease() { + return gitHubRequest(`/repos/${this.owner}/${this.repo}/releases`, this.token, { + tag_name: this.tag, + name: this.tag, + draft: true, + }) + } + + //noinspection JSUnusedGlobalSymbols + deleteRelease(): BluebirdPromise { + if (this._releasePromise.isFulfilled()) { + return gitHubRequest(`/repos/${this.owner}/${this.repo}/releases/${this._releasePromise.value().id}`, this.token, null, "DELETE") + } + else { + return BluebirdPromise.resolve() + } + } +} \ No newline at end of file diff --git a/src/gitHubRequest.ts b/src/gitHubRequest.ts new file mode 100644 index 00000000000..8658169364e --- /dev/null +++ b/src/gitHubRequest.ts @@ -0,0 +1,90 @@ +import * as https from "https" +import { RequestOptions } from "https" +import { IncomingMessage, ClientRequest } from "http" +import { addTimeOutHandler } from "./httpRequest" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export function gitHubRequest(path: string, token: string, data: { [name: string]: any; } = null, method: string = "GET"): BluebirdPromise { + const options: any = { + hostname: "api.github.com", + path: path, + method: method, + headers: { + Accept: "application/vnd.github.v3+json", + "User-Agent": "electron-complete-builder", + } + } + + const encodedData = data == null ? null : new Buffer(JSON.stringify(data)) + if (encodedData != null) { + options.method = "post" + options.headers["Content-Type"] = "application/json" + options.headers["Content-Length"] = encodedData.length + } + return doGitHubRequest(options, token, it => it.end(encodedData)) +} + +export function doGitHubRequest(options: RequestOptions, token: string, requestProcessor: (request: ClientRequest, reject: (error: Error) => void) => void): BluebirdPromise { + if (token != null) { + (options.headers).authorization = "token " + token + } + + return new BluebirdPromise((resolve, reject, onCancel) => { + const request = https.request(options, (response: IncomingMessage) => { + try { + if (response.statusCode === 404) { + // error is clear, we don't need to read detailed error description + reject(new HttpError(response)) + return + } + else if (response.statusCode === 204) { + // on DELETE request + resolve() + return + } + + let data = "" + response.setEncoding("utf8") + response.on("data", (chunk: string) => { + data += chunk + }) + + response.on("end", () => { + try { + if (response.statusCode >= 400) { + if (response.headers["content-type"].includes("json")) { + reject(new HttpError(response, JSON.parse(data))) + } + else { + reject(new HttpError(response)) + } + } + else { + resolve(data.length === 0 ? null : JSON.parse(data)) + } + } + catch (e) { + reject(e) + } + }) + } + catch (e) { + reject(e) + } + }) + addTimeOutHandler(request, reject) + request.on("error", reject) + requestProcessor(request, reject) + onCancel(() => request.abort()) + }) +} + +export class HttpError extends Error { + constructor(public response: IncomingMessage, public description: any = null) { + super(response.statusCode + " " + response.statusMessage + (description == null ? "" : ("\n" + JSON.stringify(description, null, " "))) + "\nHeaders: " + JSON.stringify(response.headers, null, " ")) + } +} \ No newline at end of file diff --git a/src/httpRequest.ts b/src/httpRequest.ts new file mode 100644 index 00000000000..d185170f415 --- /dev/null +++ b/src/httpRequest.ts @@ -0,0 +1,69 @@ +import { Socket } from "net" +import { IncomingMessage, ClientRequest } from "http" +import * as https from "https" +import { createWriteStream } from "fs" +import { parse as parseUrl } from "url" +import { Promise as BluebirdPromise } from "bluebird" + +const maxRedirects = 10 + +export const download = BluebirdPromise.promisify(_download) + +function _download(url: string, destination: string, callback: (error: Error) => void): void { + doDownload(url, destination, 0, callback) +} + +export function addTimeOutHandler(request: ClientRequest, callback: (error: Error | string) => void) { + request.on("socket", function (socket: Socket) { + socket.setTimeout(60 * 1000, () => { + callback("Request timed out") + request.abort() + }) + }) +} + +function doDownload(url: string, destination: string, redirectCount: number, callback: (error: Error) => void) { + const parsedUrl = parseUrl(url) + // user-agent must be specified, otherwise some host can return 401 unauthorised + const request = https.request({ + hostname: parsedUrl.hostname, + path: parsedUrl.path, + headers: { + "User-Agent": "electron-complete-builder" + } + }, (response: IncomingMessage) => { + if (response.statusCode >= 400) { + callback(new Error("Request error, status " + response.statusCode + ": " + response.statusMessage)) + return + } + + const redirectUrl = response.headers.location + if (redirectUrl != null) { + if (redirectCount < maxRedirects) { + doDownload(redirectUrl, destination, redirectCount++, callback) + } + else { + callback(new Error("Too many redirects (> " + maxRedirects + ")")) + } + return + } + + const downloadStream = createWriteStream(destination) + response.pipe(downloadStream) + downloadStream.on("finish", () => downloadStream.close(callback)) + + let ended = false + response.on("end", () => { + ended = true + }) + + response.on("close", () => { + if (!ended) { + callback(new Error("Request aborted")) + } + }) + }) + addTimeOutHandler(request, callback) + request.on("error", callback) + request.end() +} \ No newline at end of file diff --git a/src/install-app-deps.ts b/src/install-app-deps.ts new file mode 100644 index 00000000000..46f527ef108 --- /dev/null +++ b/src/install-app-deps.ts @@ -0,0 +1,19 @@ +#! /usr/bin/env node + +import { DEFAULT_APP_DIR_NAME, installDependencies, commonArgs, getElectronVersion } from "./util" +import { parseJson } from "./promisifed-fs" +import { printErrorAndExit } from "./promise" +import { readFileSync } from "fs" +import * as path from "path" +import cla = require("command-line-args") + +const args = cla(commonArgs.concat({ + name: "arch", + type: String, +})).parse() + +const devPackageFile = path.join(process.cwd(), "package.json") + +const appDir = args.appDir || DEFAULT_APP_DIR_NAME +installDependencies(path.join(process.cwd(), appDir), args.arch, getElectronVersion(parseJson(readFileSync(devPackageFile, "utf8"), devPackageFile), devPackageFile)) + .catch(printErrorAndExit) \ No newline at end of file diff --git a/src/linuxPackager.ts b/src/linuxPackager.ts new file mode 100644 index 00000000000..5d5637d5a27 --- /dev/null +++ b/src/linuxPackager.ts @@ -0,0 +1,57 @@ +import * as path from "path" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" +import { init } from "../lib/linux" +import { PlatformPackager, BuildInfo } from "./platformPackager" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +const buildDeb = BluebirdPromise.promisify(init().build) + +export default class LinuxPackager extends PlatformPackager { + constructor(info: BuildInfo) { + super(info) + } + + getBuildConfigurationKey() { + return "linux" + } + + packageInDistributableFormat(outDir: string, customConfiguration: DebOptions, arch: string): Promise { + const specification: DebOptions = { + version: this.metadata.version, + title: this.metadata.name, + comment: this.metadata.description, + maintainer: this.metadata.author, + arch: arch === "ia32" ? 32 : 64, + target: "deb", + executable: this.metadata.name, + } + + if (customConfiguration != null) { + Object.assign(specification, customConfiguration) + } + return buildDeb({ + log: function emptyLog() {/* ignore out */}, + appPath: outDir, + out: path.dirname(outDir), + config: { + linux: specification + } + }) + .then(it => this.dispatchArtifactCreated(it)) + } +} + +interface DebOptions { + title: string + comment: string + + version: string + + arch: number + maintainer: string + executable: string + target: string +} \ No newline at end of file diff --git a/src/macPackager.ts b/src/macPackager.ts new file mode 100644 index 00000000000..04e65c7d832 --- /dev/null +++ b/src/macPackager.ts @@ -0,0 +1,110 @@ +import { PlatformPackager, BuildInfo } from "./platformPackager" +import * as path from "path" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" +import { log, spawn } from "./util" +import { createKeychain, deleteKeychain, CodeSigningInfo, generateKeychainName, sign } from "./codeSign" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export default class MacPackager extends PlatformPackager { + codeSigningInfo: Promise + + constructor(info: BuildInfo, cleanupTasks: Array<() => Promise>) { + super(info) + + if (this.options.cscLink != null && this.options.cscKeyPassword != null) { + const keychainName = generateKeychainName() + cleanupTasks.push(() => deleteKeychain(keychainName)) + this.codeSigningInfo = createKeychain(keychainName, this.options.cscLink, this.options.cscKeyPassword) + } + else { + this.codeSigningInfo = BluebirdPromise.resolve(null) + } + } + + getBuildConfigurationKey() { + return "osx" + } + + async pack(platform: string, arch: string, outDir: string): Promise { + await super.pack(platform, arch, outDir) + let codeSigningInfo = await this.codeSigningInfo + return await this.signMac(path.join(outDir, this.metadata.name + ".app"), codeSigningInfo) + } + + private signMac(distPath: string, codeSigningInfo: CodeSigningInfo): Promise { + if (codeSigningInfo == null) { + codeSigningInfo = {cscName: this.options.sign || process.env.CSC_NAME} + } + + if (codeSigningInfo.cscName == null) { + log("App is not signed: CSC_LINK or CSC_NAME are not specified") + return BluebirdPromise.resolve() + } + else { + log("Signing app") + return sign(distPath, codeSigningInfo) + } + } + + packageInDistributableFormat(outDir: string, customConfiguration: appdmg.Specification, arch: string): Promise { + const artifactPath = path.join(outDir, this.metadata.name + "-" + this.metadata.version + ".dmg") + return BluebirdPromise.all([ + new BluebirdPromise((resolve, reject) => { + log("Creating DMG") + + const specification: appdmg.Specification = { + title: this.metadata.name, + icon: "build/icon.icns", + "icon-size": 80, + background: "build/background.png", + contents: [ + { + "x": 410, "y": 220, "type": "link", "path": "/Applications" + }, + { + "x": 130, "y": 220, "type": "file" + } + ] + } + + if (customConfiguration != null) { + Object.assign(specification, customConfiguration) + } + + if (specification.title == null) { + specification.title = this.metadata.name + } + + specification.contents[1].path = path.join(outDir, this.metadata.name + ".app") + + const appDmg = require("appdmg") + const emitter = appDmg({ + target: artifactPath, + basepath: this.projectDir, + specification: specification + }) + emitter.on("error", reject) + emitter.on("finish", () => resolve()) + }) + .then(() => this.dispatchArtifactCreated(artifactPath)), + + this.zipMacApp(outDir) + .then(it => this.dispatchArtifactCreated(it)) + ]) + } + + private zipMacApp(outDir: string): Promise { + log("Creating ZIP for Squirrel.Mac") + const appName = this.metadata.name + // -y param is important - "store symbolic links as the link instead of the referenced file" + const resultPath = `${appName}-${this.metadata.version}-mac.zip` + return spawn("zip", ["-ryXq", resultPath, appName + ".app"], { + cwd: outDir, + stdio: "inherit", + }) + .thenReturn(outDir + "/" + resultPath) + } +} \ No newline at end of file diff --git a/src/packager.ts b/src/packager.ts new file mode 100644 index 00000000000..983384dfa1e --- /dev/null +++ b/src/packager.ts @@ -0,0 +1,193 @@ +import * as fs from "fs" +import * as path from "path" +import { DEFAULT_APP_DIR_NAME, installDependencies, log, getElectronVersion } from "./util" +import { parseJsonFile } from "./promisifed-fs" +import { all, executeFinally } from "./promise" +import { EventEmitter } from "events" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" +import { AppMetadata, InfoRetriever } from "./repositoryInfo" +import { PackagerOptions, PlatformPackager, BuildInfo, DevMetadata } from "./platformPackager" +import MacPackager from "./macPackager" +import WinPackager from "./winPackager" +import LinuxPackager from "./linuxPackager" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +function addHandler(emitter: EventEmitter, event: string, handler: Function) { + emitter.on(event, handler) +} + +export class Packager implements BuildInfo { + projectDir: string + + appDir: string + + metadata: AppMetadata + devMetadata: DevMetadata + + private isTwoPackageJsonProjectLayoutUsed = true + + electronVersion: string + + eventEmitter = new EventEmitter() + + //noinspection JSUnusedLocalSymbols + constructor(public options: PackagerOptions, public repositoryInfo: InfoRetriever = null) { + this.projectDir = options.projectDir == null ? process.cwd() : path.resolve(options.projectDir) + this.appDir = this.computeAppDirectory() + } + + artifactCreated(handler: (path: string) => void): Packager { + addHandler(this.eventEmitter, "artifactCreated", handler) + return this + } + + get devPackageFile(): string { + return path.join(this.projectDir, "package.json") + } + + async build(): Promise { + const buildPackageFile = this.devPackageFile + const appPackageFile = this.projectDir === this.appDir ? buildPackageFile : path.join(this.appDir, "package.json") + await BluebirdPromise.all(Array.from(new Set([buildPackageFile, appPackageFile]), parseJsonFile)) + .then((result: any[]) => { + this.metadata = result[result.length - 1] + this.devMetadata = result[0] + this.checkMetadata(appPackageFile) + + this.electronVersion = getElectronVersion(this.devMetadata, buildPackageFile) + }) + + const cleanupTasks: Array<() => Promise> = [] + return executeFinally(this.doBuild(cleanupTasks), error => all(cleanupTasks.map(it => it()))) + } + + private async doBuild(cleanupTasks: Array<() => Promise>): Promise { + const platforms = this.options.platform === "all" ? getSupportedPlatforms() : [this.options.platform || process.platform] + const distTasks: Array> = [] + for (let platform of platforms) { + const helper = this.createHelper(platform, cleanupTasks) + const archs = platform === "darwin" ? ["x64"] : (this.options.arch == null || this.options.arch === "all" ? ["ia32", "x64"] : [this.options.arch]) + for (let arch of archs) { + await this.installAppDependencies(arch) + + const outDir = path.join(this.projectDir, "dist", this.metadata.name + "-" + platform + "-" + arch) + await helper.pack(platform, arch, outDir) + if (this.options.dist) { + const buildMetadata: any = this.devMetadata.build + const customConfiguration = buildMetadata == null ? buildMetadata : buildMetadata[helper.getBuildConfigurationKey()] + distTasks.push(helper.packageInDistributableFormat(outDir, customConfiguration, arch)) + } + } + } + + return await BluebirdPromise.all(distTasks) + } + + private createHelper(platform: string, cleanupTasks: Array<() => Promise>): PlatformPackager { + switch (platform) { + case "darwin": + case "osx": + { + const helperClass: typeof MacPackager = require("./macPackager").default + return new helperClass(this, cleanupTasks) + } + + case "win32": + case "win": + case "windows": + { + const helperClass: typeof WinPackager = require("./winPackager").default + return new helperClass(this, cleanupTasks) + } + + case "linux": + { + const helperClass: typeof LinuxPackager = require("./linuxPackager").default + return new helperClass(this) + } + + default: + throw new Error("Unsupported platform: " + platform) + } + } + + // Auto-detect app/ (two package.json project layout (development and app)) or fallback to use working directory if not explicitly specified + private computeAppDirectory(): string { + let customAppPath = this.options.appDir + let required = true + if (customAppPath == null) { + customAppPath = DEFAULT_APP_DIR_NAME + required = false + } + + let absoluteAppPath = path.join(this.projectDir, customAppPath) + try { + fs.accessSync(absoluteAppPath) + } + catch (e) { + if (required) { + throw new Error(customAppPath + " doesn't exists, " + e.message) + } + else { + this.isTwoPackageJsonProjectLayoutUsed = false + return this.projectDir + } + } + return absoluteAppPath + } + + private checkMetadata(appPackageFile: string): void { + const reportError = (missedFieldName: string) => { + throw new Error("Please specify '" + missedFieldName + "' in the application package.json ('" + appPackageFile + "')") + } + + const metadata = this.metadata + if (metadata.name == null) { + reportError("name") + } + else if (metadata.description == null) { + reportError("description") + } + else if (metadata.version == null) { + reportError("version") + } + else if (metadata.build == null) { + throw new Error("Please specify 'build' configuration in the application package.json ('" + appPackageFile + "'), at least\n\n" + + JSON.stringify({ + build: { + "app-bundle-id": "your.id", + "app-category-type": "your.app.category.type", + "iconUrl": "see https://github.com/develar/electron-complete-builder#in-short", + } + }, null, " ") + "\n\n is required.\n") + } + else if (metadata.author == null) { + reportError("author") + } + } + + private installAppDependencies(arch: string): Promise { + if (this.isTwoPackageJsonProjectLayoutUsed) { + return installDependencies(this.appDir, arch, this.electronVersion) + } + else { + log("Skipping app dependencies installation because dev and app dependencies are not separated") + return Promise.resolve(null) + } + } +} + +function getSupportedPlatforms(): string[] { + if (process.platform === "darwin") { + return ["darwin", "win32", "linux"] + } + else if (process.platform === "win32") { + return ["win32"] + } + else { + return ["linux", "win32"] + } +} diff --git a/src/platformPackager.ts b/src/platformPackager.ts new file mode 100644 index 00000000000..c047e0d4f65 --- /dev/null +++ b/src/platformPackager.ts @@ -0,0 +1,108 @@ +import { AppMetadata, InfoRetriever, ProjectMetadataProvider, Metadata } from "./repositoryInfo" +import EventEmitter = NodeJS.EventEmitter +import { tsAwaiter } from "./awaiter" +import { Promise as BluebirdPromise } from "bluebird" +import * as path from "path" +import packager = require("electron-packager-tf") + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export interface DevMetadata extends Metadata { + build: DevBuildMetadata +} + +export interface DevBuildMetadata { + osx: appdmg.Specification + win: any, + linux: any +} + +export interface PackagerOptions { + arch?: string + + dist?: boolean + githubToken?: string + + sign?: string + platform?: string + appDir?: string + + projectDir?: string + + cscLink?: string + cscKeyPassword?: string +} + +export interface BuildInfo extends ProjectMetadataProvider { + options: PackagerOptions + + projectDir: string + appDir: string + + electronVersion: string + + repositoryInfo: InfoRetriever + eventEmitter: EventEmitter +} + +export abstract class PlatformPackager implements ProjectMetadataProvider { + protected options: PackagerOptions + + protected projectDir: string + + metadata: AppMetadata + devMetadata: Metadata + + abstract getBuildConfigurationKey(): string + + constructor(protected info: BuildInfo) { + this.options = info.options + this.projectDir = info.projectDir + this.metadata = info.metadata + this.devMetadata = info.devMetadata + } + + protected dispatchArtifactCreated(path: string) { + this.info.eventEmitter.emit("artifactCreated", path) + } + + pack(platform: string, arch: string, outDir: string): Promise { + return new BluebirdPromise((resolve, reject) => { + const version = this.metadata.version + let buildVersion = version + const buildNumber = process.env.TRAVIS_BUILD_NUMBER || process.env.APPVEYOR_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM + if (buildNumber != null) { + buildVersion += "." + buildNumber + } + + const options = Object.assign({ + dir: this.info.appDir, + out: path.dirname(outDir), + name: this.metadata.name, + platform: platform, + arch: arch, + version: this.info.electronVersion, + icon: path.join(this.projectDir, "build", "icon"), + asar: true, + overwrite: true, + "app-version": version, + "build-version": buildVersion, + "version-string": { + CompanyName: this.metadata.author, + FileDescription: this.metadata.description, + ProductVersion: version, + FileVersion: buildVersion, + ProductName: this.metadata.name, + InternalName: this.metadata.name, + } + }, this.metadata.build, {"tmpdir": false}) + + // this option only for windows-installer + delete options.iconUrl + packager(options, error => error == null ? resolve(null) : reject(error)) + }) + } + + abstract packageInDistributableFormat(outDir: string, customConfiguration: DC, arch: string): Promise +} \ No newline at end of file diff --git a/src/promise.ts b/src/promise.ts new file mode 100644 index 00000000000..7677d68e236 --- /dev/null +++ b/src/promise.ts @@ -0,0 +1,61 @@ +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export function printErrorAndExit(error: Error) { + console.error(error.stack || error.message || error) + process.exit(-1) +} + +// you don't need to handle error in your task - it is passed only indicate status of promise +export async function executeFinally(promise: Promise, task: (error?: Error) => Promise): Promise { + let result: any = null + try { + result = await promise + } + catch (originalError) { + try { + await task(originalError) + } + catch (taskError) { + throw new NestedError([originalError, taskError]) + } + + throw originalError + } + + try { + await task(null) + } + catch (taskError) { + throw taskError + } + return result +} + +export class NestedError extends Error { + constructor(errors: Array, message: string = "Compound error: ") { + let m = message + let i = 1 + for (let error of errors) { + const prefix = "Error #" + i++ + " " + m += "\n\n" + prefix + "-".repeat(80) + "\n" + error.stack + } + super(m) + } +} + +export function all(promises: Array>): BluebirdPromise { + const errors: Array = [] + return BluebirdPromise.all(promises.map(it => it.catch(it => errors.push(it)))) + .then(() => { + if (errors.length === 1) { + throw errors[0] + } + else if (errors.length > 1) { + throw new NestedError(errors, "Cannot cleanup: ") + } + }) +} \ No newline at end of file diff --git a/src/promisifed-fs.ts b/src/promisifed-fs.ts new file mode 100644 index 00000000000..78892a76ffa --- /dev/null +++ b/src/promisifed-fs.ts @@ -0,0 +1,59 @@ +import * as fs from "fs" +import { parse as _parseJson } from "json-parse-helpfulerror" +import { Promise as BluebirdPromise } from "bluebird" +import rimraf = require("rimraf") + +const readFileAsync: ((filename: string, encoding?: string) => Promise) = BluebirdPromise.promisify(fs.readFile) +const writeFileAsync = BluebirdPromise.promisify(fs.writeFile) + +export function readFile(file: string): BluebirdPromise { + return >readFileAsync(file, "utf8") +} + +export function writeFile(path: string, data: string | Buffer): BluebirdPromise { + return writeFileAsync(path, data) +} + +export function parseJsonFile(file: string): BluebirdPromise { + return readFile(file). + then(it => parseJson(it, file)) +} + +export function parseJson(data: string, path: string): any { + try { + return _parseJson(data) + } + catch (e) { + if (e instanceof SyntaxError) { + throw new Error("Cannot parse '" + path + "': " + e.message) + } + else { + throw e + } + } +} + +export function deleteFile(path: string, ignoreIfNotExists: boolean = false): BluebirdPromise { + return new BluebirdPromise((resolve, reject) => { + fs.unlink(path, it => it == null || (ignoreIfNotExists && it.code === "ENOENT") ? resolve(null) : reject(it)) + }) +} + +export function deleteDirectory(path: string) { + return new BluebirdPromise((resolve, reject) => { + rimraf(path, {glob: false}, error => error == null ? resolve(null) : reject(error)) + }) +} + +// returns new name +export function renameFile(oldPath: string, newPath: string): BluebirdPromise { + return new BluebirdPromise((resolve, reject) => { + fs.rename(oldPath, newPath, error => error == null ? resolve(newPath) : reject(error)) + }) +} + +const statFileAsync = BluebirdPromise.promisify(fs.stat) + +export function stat(path: string): BluebirdPromise { + return statFileAsync(path) +} \ No newline at end of file diff --git a/src/repositoryInfo.ts b/src/repositoryInfo.ts new file mode 100644 index 00000000000..47759f34665 --- /dev/null +++ b/src/repositoryInfo.ts @@ -0,0 +1,106 @@ +import { tsAwaiter } from "./awaiter" +import { fromUrl as parseRepositoryUrl, Info } from "hosted-git-info" +import { readFile } from "./promisifed-fs" +import * as path from "path" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +interface RepositoryInfo { + url: string +} + +export interface Metadata { + repository: string | RepositoryInfo +} + +export interface AppMetadata extends Metadata { + version: string + name: string + description: string + author: string + + build: BuildMetadata + + windowsPackager: any +} + +export interface BuildMetadata { + iconUrl: string +} + +export interface ProjectMetadataProvider { + metadata: AppMetadata + devMetadata: Metadata +} + +export interface RepositorySlug { + user: string + project: string +} + +export class InfoRetriever { + _info: Promise + + getInfo(provider: ProjectMetadataProvider): Promise { + if (this._info == null) { + this._info = getInfo(provider) + } + return this._info + } +} + +async function getGitUrlFromGitConfig(): Promise { + let data: string = null + try { + data = await readFile(path.join(".git", "config")) + } + catch (e) { + if (e.code === "ENOENT") { + return null + } + + throw e + } + + const conf = data.split(/\r?\n/) + const i = conf.indexOf('[remote "origin"]') + if (i !== -1) { + let u = conf[i + 1] + if (!u.match(/^\s*url =/)) { + u = conf[i + 2] + } + + if (u.match(/^\s*url =/)) { + return u.replace(/^\s*url = /, "") + } + } + return null +} + +async function getInfo(provider: ProjectMetadataProvider): Promise { + const repo = provider.devMetadata.repository || provider.metadata.repository + if (repo == null) { + let url = process.env.TRAVIS_REPO_SLUG + if (url == null) { + const user: string = process.env.APPVEYOR_ACCOUNT_NAME || process.env.CIRCLE_PROJECT_USERNAME + const project: string = process.env.APPVEYOR_PROJECT_NAME || process.env.CIRCLE_PROJECT_REPONAME + if (user != null && project != null) { + return { + user: user, + project: project, + } + } + + url = await getGitUrlFromGitConfig() + } + + if (url != null) { + return parseRepositoryUrl(url) + } + } + else { + return parseRepositoryUrl(typeof repo === "string" ? repo : repo.url) + } + return null +} \ No newline at end of file diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 00000000000..dce100b6ff8 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,94 @@ +import { execFile, spawn as _spawn } from "child_process" +import { Promise as BluebirdPromise } from "bluebird" +import "source-map-support/register" + +export const log = console.log + +BluebirdPromise.config({ + longStackTraces: true, + cancellation: true +}) + +export const DEFAULT_APP_DIR_NAME = "app" + +export const commonArgs: any[] = [{ + name: "appDir", + type: String, + description: "Relative (to the working directory) path to the folder containing the application package.json. Working directory or app/ by default." +}] + +const execFileAsync: (file: string, args?: string[], options?: ExecOptions) => BluebirdPromise = (BluebirdPromise.promisify(execFile, {multiArgs: true})) + +export function installDependencies(appDir: string, arch: string, electronVersion: string): BluebirdPromise { + log("Installing app dependencies for arch %s to %s", arch || process.arch, appDir) + const env = Object.assign({}, process.env, { + npm_config_disturl: "https://atom.io/download/atom-shell", + npm_config_target: electronVersion, + npm_config_runtime: "electron", + HOME: require("os").homedir() + "/.electron-gyp", + }) + + if (arch != null) { + env.npm_config_arch = arch + } + + let npmExecPath = process.env.npm_execpath || process.env.NPM_CLI_JS + const npmExecArgs = ["install"] + if (npmExecPath == null) { + npmExecPath = "npm" + } + else { + npmExecArgs.unshift(npmExecPath) + npmExecPath = process.env.npm_node_execpath || process.env.NODE_EXE || "node" + } + + return spawn(npmExecPath, npmExecArgs, { + cwd: appDir, + stdio: "inherit", + env: env + }) +} + +interface BaseExecOptions { + cwd?: string + env?: any + stdio?: any +} + +interface ExecOptions extends BaseExecOptions { + customFds?: any + encoding?: string + timeout?: number + maxBuffer?: number + killSignal?: string +} + +interface SpawnOptions extends BaseExecOptions { + custom?: any + detached?: boolean +} + +export function exec(file: string, args?: string[], options?: ExecOptions): BluebirdPromise { + return execFileAsync(file, args, options) +} + +export function spawn(command: string, args?: string[], options?: SpawnOptions): BluebirdPromise { + return new BluebirdPromise((resolve, reject) => { + const p = _spawn(command, args, options) + p.on("close", (code: number) => code === 0 ? resolve() : reject(new Error(command + " exited with code " + code))) + }) +} + +export function getElectronVersion(packageData: any, filePath: string): string { + const devDependencies = packageData.devDependencies + let electronPrebuiltDep = devDependencies == null ? null : devDependencies["electron-prebuilt"] + if (electronPrebuiltDep == null) { + const dependencies = packageData.dependencies + electronPrebuiltDep = dependencies == null ? null : dependencies["electron-prebuilt"] + } + + if (electronPrebuiltDep == null) { + throw new Error("Cannot find electron-prebuilt dependency to get electron version in the '" + filePath + "'") + } + return electronPrebuiltDep.substring(1) +} \ No newline at end of file diff --git a/src/winPackager.ts b/src/winPackager.ts new file mode 100644 index 00000000000..89105eea4d1 --- /dev/null +++ b/src/winPackager.ts @@ -0,0 +1,123 @@ +import { downloadCertificate } from "./codeSign" +import { Promise as BluebirdPromise } from "bluebird" +import { tsAwaiter } from "./awaiter" +import { PlatformPackager, BuildInfo } from "./platformPackager" +import * as path from "path" +import { Stats } from "fs" +import { stat, renameFile } from "./promisifed-fs" +import { log } from "./util" +import { deleteDirectory, deleteFile } from "./promisifed-fs" + +const __awaiter = tsAwaiter +Array.isArray(__awaiter) + +export default class WinPackager extends PlatformPackager { + certFilePromise: Promise + + constructor(info: BuildInfo, cleanupTasks: Array<() => Promise>) { + super(info) + + // https://developer.mozilla.org/en-US/docs/Signing_an_executable_with_Authenticode + // https://github.com/Squirrel/Squirrel.Windows/pull/505 + if (this.options.cscLink != null && this.options.cscKeyPassword != null && process.platform !== "darwin") { + this.certFilePromise = downloadCertificate(this.options.cscLink) + .then(path => { + cleanupTasks.push(() => deleteFile(path, true)) + return path + }) + } + else { + this.certFilePromise = BluebirdPromise.resolve(null) + } + } + + getBuildConfigurationKey() { + return "win" + } + + pack(platform: string, arch: string, outDir: string): Promise { + if (this.options.dist) { + const installerOut = outDir + "-installer" + log("Removing %s", installerOut) + return BluebirdPromise.all([ + super.pack(platform, arch, outDir), + deleteDirectory(installerOut) + ]) + } + else { + return super.pack(platform, arch, outDir) + } + } + + async packageInDistributableFormat(outDir: string, customConfiguration: any, arch: string): Promise { + let iconUrl = this.metadata.build.iconUrl + if (!iconUrl) { + if (customConfiguration != null) { + iconUrl = customConfiguration.iconUrl + } + if (!iconUrl) { + if (this.info.repositoryInfo != null) { + const info = await this.info.repositoryInfo.getInfo(this) + if (info != null) { + iconUrl = `https://raw.githubusercontent.com/${info.user}/${info.project}/master/build/icon.ico` + } + } + + if (!iconUrl) { + throw new Error("iconUrl is not specified, please see https://github.com/develar/electron-complete-builder#in-short") + } + } + } + + const certificateFile = await this.certFilePromise + + const version = this.metadata.version + const outputDirectory = outDir + "-installer" + const options = Object.assign({ + name: this.metadata.name, + appDirectory: outDir, + outputDirectory: outputDirectory, + productName: this.metadata.name, + version: version, + description: this.metadata.description, + authors: this.metadata.author, + iconUrl: iconUrl, + setupIcon: path.join(this.projectDir, "build", "icon.ico"), + certificateFile: certificateFile, + certificatePassword: this.options.cscKeyPassword, + }, customConfiguration) + + try { + await new BluebirdPromise((resolve, reject) => { + require("electron-winstaller-temp-fork").build(options, (error: Error) => error == null ? resolve(null) : reject(error)) + }) + } + catch (e) { + if (e.message.indexOf("Unable to set icon") < 0) { + throw e + } + else { + let fileInfo: Stats + try { + fileInfo = await stat(options.setupIcon) + } + catch (e) { + throw new Error("Please specify correct setupIcon, file " + options.setupIcon + " not found") + } + + if (fileInfo.isDirectory()) { + throw new Error("Please specify correct setupIcon, " + options.setupIcon + " is a directory") + } + } + } + + const appName = this.metadata.name + const archSuffix = (arch === "x64") ? "-x64" : "" + return await BluebirdPromise.all([ + renameFile(path.join(outputDirectory, appName + "Setup.exe"), path.join(outputDirectory, appName + "Setup-" + version + archSuffix + ".exe")) + .then(it => this.dispatchArtifactCreated(it)), + renameFile(path.join(outputDirectory, appName + "-" + version + "-full.nupkg"), path.join(outputDirectory, appName + "-" + version + archSuffix + "-full.nupkg")) + .then(it => this.dispatchArtifactCreated(it)) + ]) + } +} \ No newline at end of file diff --git a/test/ArtifactPublisherTest.js b/test/ArtifactPublisherTest.js new file mode 100644 index 00000000000..1c5eb89bb5a --- /dev/null +++ b/test/ArtifactPublisherTest.js @@ -0,0 +1,60 @@ +import test from "ava-tf" +import { GitHubPublisher } from "../out/gitHubPublisher" +import { join } from "path" + +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +function versionNumber() { + return getRandomInt(0, 99) + "." + Date.now() + "." + getRandomInt(0, 9); +} + +const token = new Buffer("Y2Y5NDdhZDJhYzJlMzg1OGNiNzQzYzcwOWZhNGI0OTk2NWQ4ZDg3Yg==", "base64").toString() +const iconPath = join(__dirname, "fixtures", "test-app", "build", "icon.icns") + +//test("GitHub unauthorized", async (t) => { +// t.throws(await new GitHubPublisher("github-releases-test", "test-repo", versionNumber(), "incorrect token") +// .releasePromise, /(Bad credentials|Unauthorized|API rate limit exceeded)/) +//}) + +function isApiRateError(e) { + return e.description != null && e.description.message != null && e.description.message.includes("API rate limit exceeded") +} + +function testAndIgnoreApiRate(name, testFunction) { + test(name, async () => { + try { + await testFunction() + } + catch (e) { + if (isApiRateError(e)) { + console.warn(e.description.message) + } + else { + throw e + } + } + }) +} + +testAndIgnoreApiRate("GitHub upload", async () => { + const publisher = new GitHubPublisher("actperepo", "ecb2", versionNumber(), token) + try { + await publisher.upload(iconPath) + } + finally { + await publisher.deleteRelease() + } +}) + +testAndIgnoreApiRate("GitHub overwrite on upload", async () => { + const publisher = new GitHubPublisher("actperepo", "ecb2", versionNumber(), token) + try { + await publisher.upload(iconPath) + await publisher.upload(iconPath) + } + finally { + await publisher.deleteRelease() + } +}) diff --git a/test/BuildTest.js b/test/BuildTest.js new file mode 100644 index 00000000000..6b83534a8c1 --- /dev/null +++ b/test/BuildTest.js @@ -0,0 +1,125 @@ +import test from "ava-tf" +import fse from "fs-extra" +import tmp from "tmp" +import Promise from "bluebird" +import assertThat from "should/as-function" +import * as path from "path" +import { parse as parsePlist } from "plist" +import { Packager } from "../out/packager" +import { exec } from "../out/util" +import { deleteDirectory, readFile } from "../out/promisifed-fs" +import { CSC_LINK, CSC_KEY_PASSWORD } from "./helpers/codeSignData" + +const copyDir = Promise.promisify(fse.copy) +const tmpDir = Promise.promisify(tmp.dir) + +const expectedLinuxContents = [ '/', + '/opt/', + '/opt/TestApp/', + '/opt/TestApp/LICENSE', + '/opt/TestApp/LICENSES.chromium.html', + '/opt/TestApp/TestApp', + '/opt/TestApp/content_shell.pak', + '/opt/TestApp/icudtl.dat', + '/opt/TestApp/libnode.so', + '/opt/TestApp/natives_blob.bin', + '/opt/TestApp/pkgtarget', + '/opt/TestApp/resources/', + '/opt/TestApp/resources/app.asar', + '/opt/TestApp/resources/atom.asar', + '/opt/TestApp/snapshot_blob.bin', + '/opt/TestApp/version', + '/usr/', + '/usr/share/', + '/usr/share/applications/', + '/usr/share/applications/TestApp.desktop', + '/usr/share/doc/', + '/usr/share/doc/testapp/', + '/usr/share/doc/testapp/changelog.Debian.gz', + '/usr/share/icons/', + '/usr/share/icons/hicolors/' ] + +async function assertPack(projectDir, platform) { + projectDir = path.join(__dirname, "fixtures", projectDir) + // const isDoNotUseTempDir = platform === "darwin" + const isDoNotUseTempDir = true + if (!isDoNotUseTempDir) { + // non-osx test uses the same dir as osx test, but we cannot share node_modules (because tests executed in parallel) + const dir = await tmpDir({ + unsafeCleanup: true, + prefix: platform + }) + await copyDir(projectDir, dir, { + filter: function (p) { + const basename = path.basename(p) + return basename !== "dist" && basename !== "node_modules" && basename[0] !== "." + } + }) + projectDir = dir + } + + const packager = new Packager({ + projectDir: projectDir, + cscLink: CSC_LINK, + cscKeyPassword: CSC_KEY_PASSWORD, + dist: true, + platform: platform, + }) + + await packager.build() + if (platform === "darwin") { + const packedAppDir = projectDir + "/dist/TestApp-darwin-x64/TestApp.app" + const info = parsePlist(await readFile(packedAppDir + "/Contents/Info.plist", "utf8")) + assertThat(info).has.properties({ + CFBundleDisplayName: "TestApp", + CFBundleIdentifier: "your.id", + LSApplicationCategoryType: "your.app.category.type", + CFBundleVersion: "1.0.0" + "." + (process.env.TRAVIS_BUILD_NUMBER || process.env.CIRCLE_BUILD_NUM) + }) + + const result = await exec("codesign", ["--verify", packedAppDir]) + assertThat(result[0].toString()).not.match(/is not signed at all/) + } + else if (platform === "linux") { + assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb")).deepEqual(expectedLinuxContents) + assertThat(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb")).deepEqual(expectedLinuxContents) + // console.log(await getContents(projectDir + "/dist/TestApp-1.0.0-amd64.deb")) + // console.log(await getContents(projectDir + "/dist/TestApp-1.0.0-i386.deb")) + } +} + +async function getContents(path) { + const result = await exec("dpkg", ["--contents", path]) + return result[0] + .split("\n") + .map(it => it.length === 0 ? null : it.substring(it.indexOf('.') + 1)) + .filter(it => it != null && !(it.startsWith("/opt/TestApp/locales/") || it.startsWith("/opt/TestApp/libgcrypt"))) + .sort() +} + +if (process.env.TRAVIS !== "true") { + // we don't use CircleCI, so, we can safely set this env + process.env.CIRCLE_BUILD_NUM = 42 +} + +if (process.platform === "darwin") { + test("mac: two-package.json", async function () { + await assertPack("test-app", "darwin") + }) + + test("mac: one-package.json", async function () { + await assertPack("test-app-one", "darwin") + }) +} + +if (process.platform !== "win32") { + test("linux", async function () { + await assertPack("test-app-one", "linux") + }) +} + +if (!process.env.TRAVIS) { + test("win", async function () { + await assertPack("test-app-one", "win32") + }) +} diff --git a/test/CodeSignTest.js b/test/CodeSignTest.js new file mode 100644 index 00000000000..f86224d2a76 --- /dev/null +++ b/test/CodeSignTest.js @@ -0,0 +1,23 @@ +import { createKeychain, deleteKeychain, generateKeychainName } from "../out/codeSign" +import assertThat from "should/as-function" +import avaTest from "ava-tf" +import { CSC_NAME, CSC_LINK, CSC_KEY_PASSWORD } from "./helpers/codeSignData" +import promises from "../out/promise" + +function test(doNotSkip, name, testFunction) { + if (doNotSkip) { + avaTest(name, testFunction) + } + else { + avaTest.skip(name, testFunction) + } +} + +test(process.platform === "darwin", "create keychain", async () => { + const keychainName = generateKeychainName() + await promises.executeFinally(createKeychain(keychainName, CSC_LINK, CSC_KEY_PASSWORD) + .then(result => { + assertThat(result.cscKeychainName).not.empty() + assertThat(result.cscName).equal(CSC_NAME) + }), error => promises.all([deleteKeychain(keychainName)])) +}) \ No newline at end of file diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000000..43cb5a0068b --- /dev/null +++ b/test/README.md @@ -0,0 +1,13 @@ +# Running Windows tests on OS X + +``` +brew install Caskroom/cask/xquartz wine mono +``` + +# Running Linux tests on OS X +Do not use OS X bundled Ruby. Install using `brew`. + +``` +brew install ruby gnu-tar dpkg +gem install fpm +``` \ No newline at end of file diff --git a/test/RepoSlugTest.js b/test/RepoSlugTest.js new file mode 100644 index 00000000000..d6ecab410e4 --- /dev/null +++ b/test/RepoSlugTest.js @@ -0,0 +1,53 @@ +import { InfoRetriever } from "../out/repositoryInfo" +import assertThat from "should/as-function" +import test from "ava-tf" + +test("repo slug from TRAVIS_REPO_SLUG", function () { + const oldValue = process.env.TRAVIS_REPO_SLUG + try { + process.env.TRAVIS_REPO_SLUG = "travis-ci/travis-build" + const info = (new InfoRetriever()).getInfo({devMetadata: {}, metadata: {}}).value() + assertThat(info).has.properties({ + user: "travis-ci", + project: "travis-build", + }) + } + finally { + if (oldValue != null) { + restoreEnv("TRAVIS_REPO_SLUG", oldValue) + } + } +}) + +function restoreEnv(name, value) { + if (value != null) { + // otherwise will be set to string value "undefined" + process.env[name] = value + } +} + +test("repo slug from APPVEYOR", function () { + const oldAppveyorAccountName = process.env.APPVEYOR_ACCOUNT_NAME + const oldAppveyorProjectName = process.env.APPVEYOR_PROJECT_NAME + const travisSlug = process.env.TRAVIS_REPO_SLUG + try { + if (travisSlug != null) { + delete process.env.TRAVIS_REPO_SLUG + } + + process.env.APPVEYOR_ACCOUNT_NAME = "travis-ci" + process.env.APPVEYOR_PROJECT_NAME = "travis-build" + const info = (new InfoRetriever()).getInfo({devMetadata: {}, metadata: {}}).value() + assertThat(info).has.properties({ + user: "travis-ci", + project: "travis-build", + }) + } + finally { + restoreEnv("APPVEYOR_ACCOUNT_NAME", oldAppveyorAccountName) + restoreEnv("APPVEYOR_PROJECT_NAME", oldAppveyorProjectName) + if (travisSlug != null) { + process.env.TRAVIS_REPO_SLUG = travisSlug + } + } +}) \ No newline at end of file diff --git a/test/fixtures/test-app-one/build/background.png b/test/fixtures/test-app-one/build/background.png new file mode 100644 index 0000000000000000000000000000000000000000..28d36132eb6461e76292ddb5fed2330a83bdb625 GIT binary patch literal 9039 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 AqyPW_ literal 0 HcmV?d00001 diff --git a/test/fixtures/test-app-one/build/icon.ico b/test/fixtures/test-app-one/build/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..66c8e1745d2aa9b85c47c0925eba14e9c7be0a30 GIT binary patch literal 370070 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/test-app-one/index.html b/test/fixtures/test-app-one/index.html new file mode 100644 index 00000000000..8d710f08e6d --- /dev/null +++ b/test/fixtures/test-app-one/index.html @@ -0,0 +1,13 @@ + + + + + Hello World! + + +

Hello World!

+ We are using node , + Chrome , + and Electron . + + diff --git a/test/fixtures/test-app-one/index.js b/test/fixtures/test-app-one/index.js new file mode 100644 index 00000000000..a9b31753019 --- /dev/null +++ b/test/fixtures/test-app-one/index.js @@ -0,0 +1,51 @@ +'use strict'; + +const electron = require('electron'); +// Module to control application life. +const app = electron.app; +// Module to create native browser window. +const BrowserWindow = electron.BrowserWindow; + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the JavaScript object is garbage collected. +let mainWindow; + +function createWindow () { + // Create the browser window. + mainWindow = new BrowserWindow({width: 800, height: 600}); + + // and load the index.html of the app. + mainWindow.loadURL('file://' + __dirname + '/index.html'); + + // Open the DevTools. + mainWindow.webContents.openDevTools(); + + // Emitted when the window is closed. + mainWindow.on('closed', function() { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + mainWindow = null; + }); +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +app.on('ready', createWindow); + +// Quit when all windows are closed. +app.on('window-all-closed', function () { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', function () { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (mainWindow === null) { + createWindow(); + } +}); diff --git a/test/fixtures/test-app-one/package.json b/test/fixtures/test-app-one/package.json new file mode 100644 index 00000000000..c19b9ca22d9 --- /dev/null +++ b/test/fixtures/test-app-one/package.json @@ -0,0 +1,22 @@ +{ + "private": true, + "name": "TestApp", + "version": "1.0.0", + "description": "Test Application", + "scripts": { + "start": "electron ." + }, + "author": "Foo Bar", + "devDependencies": { + "electron-prebuilt": "^0.36.7" + }, + "build": { + "app-bundle-id": "your.id", + "app-category-type": "your.app.category.type", + "iconUrl": "https://raw.githubusercontent.com/szwacz/electron-boilerplate/master/resources/windows/icon.ico", + "linux": { + "//": "gz is much faster, in tests we don't need efficient compression", + "compression": "gz" + } + } +} diff --git a/test/fixtures/test-app/app/index.html b/test/fixtures/test-app/app/index.html new file mode 100644 index 00000000000..8d710f08e6d --- /dev/null +++ b/test/fixtures/test-app/app/index.html @@ -0,0 +1,13 @@ + + + + + Hello World! + + +

Hello World!

+ We are using node , + Chrome , + and Electron . + + diff --git a/test/fixtures/test-app/app/index.js b/test/fixtures/test-app/app/index.js new file mode 100644 index 00000000000..a9b31753019 --- /dev/null +++ b/test/fixtures/test-app/app/index.js @@ -0,0 +1,51 @@ +'use strict'; + +const electron = require('electron'); +// Module to control application life. +const app = electron.app; +// Module to create native browser window. +const BrowserWindow = electron.BrowserWindow; + +// Keep a global reference of the window object, if you don't, the window will +// be closed automatically when the JavaScript object is garbage collected. +let mainWindow; + +function createWindow () { + // Create the browser window. + mainWindow = new BrowserWindow({width: 800, height: 600}); + + // and load the index.html of the app. + mainWindow.loadURL('file://' + __dirname + '/index.html'); + + // Open the DevTools. + mainWindow.webContents.openDevTools(); + + // Emitted when the window is closed. + mainWindow.on('closed', function() { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + mainWindow = null; + }); +} + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +app.on('ready', createWindow); + +// Quit when all windows are closed. +app.on('window-all-closed', function () { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('activate', function () { + // On OS X it's common to re-create a window in the app when the + // dock icon is clicked and there are no other windows open. + if (mainWindow === null) { + createWindow(); + } +}); diff --git a/test/fixtures/test-app/app/package.json b/test/fixtures/test-app/app/package.json new file mode 100644 index 00000000000..9e35872a55b --- /dev/null +++ b/test/fixtures/test-app/app/package.json @@ -0,0 +1,16 @@ +{ + "private": true, + "name": "TestApp", + "version": "1.0.0", + "description": "Test Application", + "author": "Foo Bar", + "homepage": "https://github.com/atom/electron-quick-start#readme", + "dependencies": { + "nslog": "^3.0.0" + }, + "build": { + "app-bundle-id": "your.id", + "app-category-type": "your.app.category.type", + "iconUrl": "https://raw.githubusercontent.com/szwacz/electron-boilerplate/master/resources/windows/icon.ico" + } +} diff --git a/test/fixtures/test-app/build/background.png b/test/fixtures/test-app/build/background.png new file mode 100644 index 0000000000000000000000000000000000000000..28d36132eb6461e76292ddb5fed2330a83bdb625 GIT binary patch literal 9039 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 AqyPW_ literal 0 HcmV?d00001 diff --git a/test/fixtures/test-app/build/icon.ico b/test/fixtures/test-app/build/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..66c8e1745d2aa9b85c47c0925eba14e9c7be0a30 GIT binary patch literal 370070 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 literal 0 HcmV?d00001 diff --git a/test/fixtures/test-app/package.json b/test/fixtures/test-app/package.json new file mode 100644 index 00000000000..2c969fe45d2 --- /dev/null +++ b/test/fixtures/test-app/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "scripts": { + "start": "electron ." + }, + "devDependencies": { + "electron-prebuilt": "^0.36.7" + } +} diff --git a/test/helpers/codeSignData.js b/test/helpers/codeSignData.js new file mode 100644 index 00000000000..a4a9121a44a --- /dev/null +++ b/test/helpers/codeSignData.js @@ -0,0 +1,3 @@ +module.exports.CSC_LINK = "https://www.dropbox.com/s/86zaffzbao198xe/test.p12?dl=1" +module.exports.CSC_KEY_PASSWORD = "password" +module.exports.CSC_NAME = "Test Test" \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000000..bb08c7aa7b1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,61 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "noImplicitAny": true, + "removeComments": true, + "outDir": "out", + "newLine": "LF", + "noResolve": true, + "noEmitOnError": true, + "inlineSources": true, + "sourceMap": true, + "noImplicitReturns": true, + "noEmitHelpers": true, + "noFallthroughCasesInSwitch": true + }, + "filesGlob": [ + "src/*.ts", + "lib/*.d.ts", + "typings/**/*.d.ts", + "!typings/browser/**/*.d.ts", + "!typings/browser.d.ts", + "!typings/main.d.ts" + ], + "files": [ + "lib/linux.d.ts", + "typings/appdmg.d.ts", + "typings/bluebird.d.ts", + "typings/command-line-args.d.ts", + "typings/electron-packager.d.ts", + "typings/gh-api.d.ts", + "typings/hosted-git-info.d.ts", + "typings/json-parse-helpfulerror.d.ts", + "typings/main/ambient/fs-extra/fs-extra.d.ts", + "typings/main/ambient/mime/mime.d.ts", + "typings/main/ambient/progress/progress.d.ts", + "typings/main/ambient/should/should.d.ts", + "typings/main/ambient/tmp/tmp.d.ts", + "typings/main/definitions/source-map-support/source-map-support.d.ts", + "typings/node.d.ts", + "typings/progress-stream.d.ts", + "typings/rimraf.d.ts", + "src/awaiter.ts", + "src/build-cli.ts", + "src/builder.ts", + "src/codeSign.ts", + "src/gitHubPublisher.ts", + "src/gitHubRequest.ts", + "src/httpRequest.ts", + "src/install-app-deps.ts", + "src/linuxPackager.ts", + "src/macPackager.ts", + "src/packager.ts", + "src/platformPackager.ts", + "src/promise.ts", + "src/promisifed-fs.ts", + "src/repositoryInfo.ts", + "src/util.ts", + "src/winPackager.ts" + ] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000000..21b4619e04f --- /dev/null +++ b/tslint.json @@ -0,0 +1,66 @@ +{ + "rules": { + "class-name": true, + "curly": true, + "indent": [ + true, + "spaces" + ], + "member-ordering": [ + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-duplicate-key": true, + "no-consecutive-blank-lines": true, + "no-duplicate-variable": true, + "no-eval": true, + "no-empty": true, + "no-unreachable": true, + "no-unused-expression": true, + "no-unused-variable": [ + true + ], + "no-use-before-declare": true, + "no-internal-module": true, + "no-trailing-whitespace": true, + "no-var-keyword": true, + "no-var-requires": true, + "one-line": [ + true, + "check-open-brace", + "check-whitespace" + ], + "quotemark": [ + true, + "double", + "avoid-escape" + ], + "triple-equals": [ + true, + "allow-null-check" + ], + "typedef-whitespace": [ + true, + { + "call-signature": "nospace", + "index-signature": "nospace", + "parameter": "nospace", + "property-declaration": "nospace", + "variable-declaration": "nospace" + } + ], + "variable-name": [ + true, + "ban-keywords" + ], + "whitespace": [ + true, + "check-branch", + "check-decl", + "check-operator", + "check-separator", + "check-type" + ] + } +} \ No newline at end of file diff --git a/typings.json b/typings.json new file mode 100644 index 00000000000..719e3adc1e9 --- /dev/null +++ b/typings.json @@ -0,0 +1,12 @@ +{ + "ambientDependencies": { + "fs-extra": "github:DefinitelyTyped/DefinitelyTyped/fs-extra/fs-extra.d.ts#72d95b38f008fed506c6cbf3af8a693bd7c3363a", + "mime": "github:DefinitelyTyped/DefinitelyTyped/mime/mime.d.ts#cb5206a8ac1c9a3ddfd126f5ecea6729b2361452", + "progress": "github:DefinitelyTyped/DefinitelyTyped/progress/progress.d.ts#d54b18e0ac3277376700b6026ef9e9e3f380df50", + "should": "github:DefinitelyTyped/DefinitelyTyped/should/should.d.ts#5aa6dff6990465c7fb41504538ad7aa133ab01d3", + "tmp": "github:DefinitelyTyped/DefinitelyTyped/tmp/tmp.d.ts#48f20e97bfaf70fc1a9537b38aed98e9749be0ae" + }, + "dependencies": { + "source-map-support": "github:typed-typings/npm-source-map-support#900ed4180a22285bce4bbabc0760427e71a59eca" + } +} diff --git a/typings/appdmg.d.ts b/typings/appdmg.d.ts new file mode 100644 index 00000000000..279d0b9f180 --- /dev/null +++ b/typings/appdmg.d.ts @@ -0,0 +1,24 @@ +declare namespace appdmg { + interface Specification { + title: string + background: string + icon: string + "icon-size": number + + contents: Array + } + + interface Options { + target: string + basepath: string + specification: appdmg.Specification + } +} + +declare module "appdmg" { + import { EventEmitter } from "events" + + function appdmg(options: appdmg.Options): EventEmitter + + export = appdmg +} \ No newline at end of file diff --git a/typings/bluebird.d.ts b/typings/bluebird.d.ts new file mode 100644 index 00000000000..66f7676eb8a --- /dev/null +++ b/typings/bluebird.d.ts @@ -0,0 +1,63 @@ +// Type definitions for bluebird 2.0.0 +// Project: https://github.com/petkaantonov/bluebird +// Definitions by: Bart van der Schoor , falsandtru +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +declare module 'bluebird' { + interface Disposer { + } + + class BluebirdPromise implements Promise { + constructor(callback: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: Error) => void, onCancel?: (handler: () => void) => void) => void) + + static config(options: any): void + + static all(values: Iterable>): BluebirdPromise + + static mapSeries(items: Iterable, mapper: (item: T) => BluebirdPromise): BluebirdPromise + + static reject(error: Error): BluebirdPromise + + static coroutine(generator: Function): Function + + /** + * Returns a function that will wrap the given `nodeFunction`. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument. + * + * If the `nodeFunction` calls its callback with multiple success values, the fulfillment value will be an array of them. + * + * If you pass a `receiver`, the `nodeFunction` will be called as a method on the `receiver`. + */ + static promisify(func: (callback: (err: any, result: T) => void) => void, receiver?: any): () => BluebirdPromise; + static promisify(func: (arg1: A1, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1) => BluebirdPromise; + static promisify(func: (arg1: A1, arg2: A2, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2) => BluebirdPromise; + static promisify(func: (arg1: A1, arg2: A2, callback: (error: Error) => void) => void, receiver?: any): (arg1: A1, arg2: A2) => BluebirdPromise; + static promisify(func: (arg1: A1, arg2: A2, arg3: A3, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3) => BluebirdPromise; + static promisify(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4) => BluebirdPromise; + static promisify(func: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, callback: (err: any, result: T) => void) => void, receiver?: any): (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) => BluebirdPromise; + static promisify(nodeFunction: Function, receiver?: any): Function; + + static resolve(value: T | PromiseLike): BluebirdPromise + static resolve(): BluebirdPromise + + then(fulfilled: (value: T) => R | PromiseLike, rejected?: (reason: any) => R | PromiseLike): BluebirdPromise + + //noinspection ReservedWordAsName + catch(onrejected?: (reason: any) => T | PromiseLike): BluebirdPromise + //noinspection ReservedWordAsName + catch(onrejected?: (reason: any) => void): BluebirdPromise; + + [Symbol.toStringTag]: any + + disposer(disposer: (result: T, promise: Promise) => Promise | void): Disposer + + thenReturn(result: T): BluebirdPromise + + cancel(): void + + isFulfilled(): boolean + + value(): T + } + + export { BluebirdPromise as Promise } +} \ No newline at end of file diff --git a/typings/command-line-args.d.ts b/typings/command-line-args.d.ts new file mode 100644 index 00000000000..55ee2c6c838 --- /dev/null +++ b/typings/command-line-args.d.ts @@ -0,0 +1,15 @@ +declare module "command-line-args" { + interface Options { + parse(): any + + getUsage(options: UsageOptions): any + } + + interface UsageOptions { + hide: Array + } + + function describe(options: Array): Options + + export = describe +} \ No newline at end of file diff --git a/typings/electron-packager.d.ts b/typings/electron-packager.d.ts new file mode 100644 index 00000000000..b6a167332b9 --- /dev/null +++ b/typings/electron-packager.d.ts @@ -0,0 +1,115 @@ +// Type definitions for electron-packager v5.1.0 +// Project: https://github.com/maxogden/electron-packager +// Definitions by: Maxime LUCE +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/// + +declare namespace ElectronPackager { + /** Electron-packager Options. */ + export interface Options { + /** The source directory. */ + dir: string; + /** The application name. */ + name: string; + /** + * Allowed values: linux, win32, darwin, all. Not required if `all` is used. + * Arbitrary combinations of individual platforms are also supported via a comma-delimited string or array of strings. + */ + platform?: string | string[]; + /** Allowed values: ia32, x64, all Not required if `all` is used. */ + arch?: string; + /** Electron version (without the "v"). See https://github.com/atom/electron/releases. */ + version: string; + + /** Shortcut for `--arch=all --platform=all`. */ + all?: boolean; + /** The output directory. */ + out?: string; + /** + * Currently you must look for conversion tools in order to supply an icon in the format required by the platform: + * - OS X: `.icns` + * - Windows: `.ico` + * + * For Linux builds, this option is not required, as the dock/window list icon is set via the icon option in the BrowserWindow contructor. + * Setting the icon in the file manager is not currently supported. + * + * If the file extension is omitted, it is auto-completed to the correct extension based on the platform, + * including when `--platform=all` is in effect. + */ + icon?: string; + + /** The bundle identifier to use in the app plist. */ + "app-bundle-id"?: string; + /** The release version to set for the app. */ + "app-version"?: string; + /** The build version to set for the app (OS X only). */ + "build-version"?: string; + /** The bundle identifier to use in the app helper plist. */ + "helper-bundle-id"?: string; + /** Object hash of application metadata to embed into the executable (Windows only). */ + "version-string"?: VersionString; + + /** The directory of cached electron downloads. Defaults to "$HOME/.electron". */ + cache?: string; + /** Do not copy files into App whose filenames regex .match this string. */ + ignore?: RegExp; + /** Runs `npm prune --production` on the app. */ + prune?: boolean; + /** If output directory for a platform already exists, replaces it rather than skipping it. */ + overwrite?: boolean; + /** Packages the source code within your app into an archive. */ + asar?: boolean; + /** Unpacks the files to app.asar.unpacked directory whose filenames regex .match this string. */ + "asar-unpack"?: string; + /** Should contain the identity to be used when running `codesign` (OS X only). */ + sign?: string; + } + + /** Object hash of application metadata to embed into the executable (Windows only). */ + export interface VersionString { + CompanyName?: string; + LegalCopyright?: string; + FileDescription?: string; + OriginalFilename?: string; + FileVersion?: string; + ProductVersion?: string; + ProductName?: string; + InternalName?: string; + } + + /** Electron-packager done callback. */ + export interface Callback { + /** + * Callback wich is called when electron-packager is done. + * + * @param err - Contains errors if any. + * @param appPath - Path to the newly created application. + */ + (err: Error, appPath: string): void + } + + /** Electron-packager function */ + export interface Packager { + /** + * This will: + * - Find or download the correct release of Electron + * - Use that version of electron to create a app in /-- + * + * You should be able to launch the app on the platform you built for. If not, check your settings and try again. + * + * @param opts - Options to configure packaging. + * @param callback - Callback which is called when packaging is done or an error occured. + */ + (opts: Options, callback: Callback): void; + } +} + +declare module "electron-packager-tf" { + const packager: ElectronPackager.Packager; + export = packager; +} + +interface NodeRequireFunction { + (id: "electron-packager"): ElectronPackager.Packager; +} diff --git a/typings/gh-api.d.ts b/typings/gh-api.d.ts new file mode 100644 index 00000000000..601b4c79edf --- /dev/null +++ b/typings/gh-api.d.ts @@ -0,0 +1,15 @@ +declare module "gh-release" { + interface Release { + id: number + tag_name: string + + draft: boolean + + upload_url: string + } + + interface Asset { + id: number + name: string + } +} \ No newline at end of file diff --git a/typings/hosted-git-info.d.ts b/typings/hosted-git-info.d.ts new file mode 100644 index 00000000000..0d122bc67bd --- /dev/null +++ b/typings/hosted-git-info.d.ts @@ -0,0 +1,10 @@ +declare module "hosted-git-info" { + interface Info { + type: string + domain: string + user: string + project: string + } + + function fromUrl(url: string): Info +} \ No newline at end of file diff --git a/typings/json-parse-helpfulerror.d.ts b/typings/json-parse-helpfulerror.d.ts new file mode 100644 index 00000000000..773f1cf7152 --- /dev/null +++ b/typings/json-parse-helpfulerror.d.ts @@ -0,0 +1,3 @@ +declare module "json-parse-helpfulerror" { + export function parse(data: string): any +} \ No newline at end of file diff --git a/typings/main/ambient/fs-extra/fs-extra.d.ts b/typings/main/ambient/fs-extra/fs-extra.d.ts new file mode 100644 index 00000000000..0c94f31b73e --- /dev/null +++ b/typings/main/ambient/fs-extra/fs-extra.d.ts @@ -0,0 +1,206 @@ +// Compiled using typings@0.6.8 +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/72d95b38f008fed506c6cbf3af8a693bd7c3363a/fs-extra/fs-extra.d.ts +// Type definitions for fs-extra +// Project: https://github.com/jprichardson/node-fs-extra +// Definitions by: midknight41 +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// Imported from: https://github.com/soywiz/typescript-node-definitions/fs-extra.d.ts + + +declare module "fs-extra" { + import stream = require("stream"); + + export interface Stats { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atime: Date; + mtime: Date; + ctime: Date; + } + + export interface FSWatcher { + close(): void; + } + + export class ReadStream extends stream.Readable { } + export class WriteStream extends stream.Writable { } + + //extended methods + export function copy(src: string, dest: string, callback?: (err: Error) => void): void; + export function copy(src: string, dest: string, filter: (src: string) => boolean, callback?: (err: Error) => void): void; + + export function copySync(src: string, dest: string): void; + export function copySync(src: string, dest: string, filter: (src: string) => boolean): void; + + export function createFile(file: string, callback?: (err: Error) => void): void; + export function createFileSync(file: string): void; + + export function mkdirs(dir: string, callback?: (err: Error) => void): void; + export function mkdirp(dir: string, callback?: (err: Error) => void): void; + export function mkdirs(dir: string, options?: MkdirOptions, callback?: (err: Error) => void): void; + export function mkdirp(dir: string, options?: MkdirOptions, callback?: (err: Error) => void): void; + export function mkdirsSync(dir: string, options?: MkdirOptions): void; + export function mkdirpSync(dir: string, options?: MkdirOptions): void; + + export function outputFile(file: string, data: any, callback?: (err: Error) => void): void; + export function outputFileSync(file: string, data: any): void; + + export function outputJson(file: string, data: any, callback?: (err: Error) => void): void; + export function outputJSON(file: string, data: any, callback?: (err: Error) => void): void; + export function outputJsonSync(file: string, data: any): void; + export function outputJSONSync(file: string, data: any): void; + + export function readJson(file: string, callback: (err: Error, jsonObject: any) => void): void; + export function readJson(file: string, options: OpenOptions, callback: (err: Error, jsonObject: any) => void): void; + export function readJSON(file: string, callback: (err: Error, jsonObject: any) => void): void; + export function readJSON(file: string, options: OpenOptions, callback: (err: Error, jsonObject: any) => void): void; + + export function readJsonSync(file: string, options?: OpenOptions): any; + export function readJSONSync(file: string, options?: OpenOptions): any; + + export function remove(dir: string, callback?: (err: Error) => void): void; + export function removeSync(dir: string): void; + // export function delete(dir: string, callback?: (err: Error) => void): void; + // export function deleteSync(dir: string): void; + + export function writeJson(file: string, object: any, callback?: (err: Error) => void): void; + export function writeJson(file: string, object: any, options?: OpenOptions, callback?: (err: Error) => void): void; + export function writeJSON(file: string, object: any, callback?: (err: Error) => void): void; + export function writeJSON(file: string, object: any, options?: OpenOptions, callback?: (err: Error) => void): void; + + export function writeJsonSync(file: string, object: any, options?: OpenOptions): void; + export function writeJSONSync(file: string, object: any, options?: OpenOptions): void; + + export function rename(oldPath: string, newPath: string, callback?: (err: Error) => void): void; + export function renameSync(oldPath: string, newPath: string): void; + export function truncate(fd: number, len: number, callback?: (err: Error) => void): void; + export function truncateSync(fd: number, len: number): void; + export function chown(path: string, uid: number, gid: number, callback?: (err: Error) => void): void; + export function chownSync(path: string, uid: number, gid: number): void; + export function fchown(fd: number, uid: number, gid: number, callback?: (err: Error) => void): void; + export function fchownSync(fd: number, uid: number, gid: number): void; + export function lchown(path: string, uid: number, gid: number, callback?: (err: Error) => void): void; + export function lchownSync(path: string, uid: number, gid: number): void; + export function chmod(path: string, mode: number, callback?: (err: Error) => void): void; + export function chmod(path: string, mode: string, callback?: (err: Error) => void): void; + export function chmodSync(path: string, mode: number): void; + export function chmodSync(path: string, mode: string): void; + export function fchmod(fd: number, mode: number, callback?: (err: Error) => void): void; + export function fchmod(fd: number, mode: string, callback?: (err: Error) => void): void; + export function fchmodSync(fd: number, mode: number): void; + export function fchmodSync(fd: number, mode: string): void; + export function lchmod(path: string, mode: string, callback?: (err: Error) => void): void; + export function lchmod(path: string, mode: number, callback?: (err: Error) => void): void; + export function lchmodSync(path: string, mode: number): void; + export function lchmodSync(path: string, mode: string): void; + export function stat(path: string, callback?: (err: Error, stats: Stats) => void): void; + export function lstat(path: string, callback?: (err: Error, stats: Stats) => void): void; + export function fstat(fd: number, callback?: (err: Error, stats: Stats) => void): void; + export function statSync(path: string): Stats; + export function lstatSync(path: string): Stats; + export function fstatSync(fd: number): Stats; + export function link(srcpath: string, dstpath: string, callback?: (err: Error) => void): void; + export function linkSync(srcpath: string, dstpath: string): void; + export function symlink(srcpath: string, dstpath: string, type?: string, callback?: (err: Error) => void): void; + export function symlinkSync(srcpath: string, dstpath: string, type?: string): void; + export function readlink(path: string, callback?: (err: Error, linkString: string) => void): void; + export function realpath(path: string, callback?: (err: Error, resolvedPath: string) => void): void; + export function realpath(path: string, cache: string, callback: (err: Error, resolvedPath: string) => void): void; + export function realpathSync(path: string, cache?: boolean): string; + export function unlink(path: string, callback?: (err: Error) => void): void; + export function unlinkSync(path: string): void; + export function rmdir(path: string, callback?: (err: Error) => void): void; + export function rmdirSync(path: string): void; + export function mkdir(path: string, mode?: number, callback?: (err: Error) => void): void; + export function mkdir(path: string, mode?: string, callback?: (err: Error) => void): void; + export function mkdirSync(path: string, mode?: number): void; + export function mkdirSync(path: string, mode?: string): void; + export function readdir(path: string, callback?: (err: Error, files: string[]) => void ): void; + export function readdirSync(path: string): string[]; + export function close(fd: number, callback?: (err: Error) => void): void; + export function closeSync(fd: number): void; + export function open(path: string, flags: string, mode?: string, callback?: (err: Error, fs: number) => void): void; + export function openSync(path: string, flags: string, mode?: string): number; + export function utimes(path: string, atime: number, mtime: number, callback?: (err: Error) => void): void; + export function utimesSync(path: string, atime: number, mtime: number): void; + export function futimes(fd: number, atime: number, mtime: number, callback?: (err: Error) => void): void; + export function futimesSync(fd: number, atime: number, mtime: number): void; + export function fsync(fd: number, callback?: (err: Error) => void): void; + export function fsyncSync(fd: number): void; + export function write(fd: number, buffer: NodeBuffer, offset: number, length: number, position: number, callback?: (err: Error, written: number, buffer: NodeBuffer) => void): void; + export function writeSync(fd: number, buffer: NodeBuffer, offset: number, length: number, position: number): number; + export function read(fd: number, buffer: NodeBuffer, offset: number, length: number, position: number, callback?: (err: Error, bytesRead: number, buffer: NodeBuffer) => void ): void; + export function readSync(fd: number, buffer: NodeBuffer, offset: number, length: number, position: number): number; + export function readFile(filename: string, encoding: string, callback: (err: Error, data: string) => void ): void; + export function readFile(filename: string, options: OpenOptions, callback: (err: Error, data: string) => void ): void; + export function readFile(filename: string, callback: (err: Error, data: NodeBuffer) => void ): void; + export function readFileSync(filename: string): NodeBuffer; + export function readFileSync(filename: string, encoding: string): string; + export function readFileSync(filename: string, options: OpenOptions): string; + export function writeFile(filename: string, data: any, encoding?: string, callback?: (err: Error) => void): void; + export function writeFile(filename: string, data: any, options?: OpenOptions, callback?: (err: Error) => void): void; + export function writeFileSync(filename: string, data: any, encoding?: string): void; + export function writeFileSync(filename: string, data: any, option?: OpenOptions): void; + export function appendFile(filename: string, data: any, encoding?: string, callback?: (err: Error) => void): void; + export function appendFile(filename: string, data: any,option?: OpenOptions, callback?: (err: Error) => void): void; + export function appendFileSync(filename: string, data: any, encoding?: string): void; + export function appendFileSync(filename: string, data: any, option?: OpenOptions): void; + export function watchFile(filename: string, listener: { curr: Stats; prev: Stats; }): void; + export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: { curr: Stats; prev: Stats; }): void; + export function unwatchFile(filename: string, listener?: Stats): void; + export function watch(filename: string, options?: { persistent?: boolean; }, listener?: (event: string, filename: string) => any): FSWatcher; + export function exists(path: string, callback?: (exists: boolean) => void ): void; + export function existsSync(path: string): boolean; + export function ensureDir(path: string, cb: (err: Error) => void): void; + export function ensureDirSync(path: string): void; + export function ensureFile(path: string, cb: (err: Error) => void): void; + export function ensureFileSync(path: string): void; + export function ensureLink(path: string, cb: (err: Error) => void): void; + export function ensureLinkSync(path: string): void; + export function ensureSymlink(path: string, cb: (err: Error) => void): void; + export function ensureSymlinkSync(path: string): void; + export function emptyDir(path: string, callback?: (err: Error) => void): void; + export function emptyDirSync(path: string): boolean; + + export interface OpenOptions { + encoding?: string; + flag?: string; + } + + export interface MkdirOptions { + fs?: any; + mode?: number; + } + + export interface ReadStreamOptions { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + bufferSize?: number; + } + export interface WriteStreamOptions { + flags?: string; + encoding?: string; + string?: string; + } + export function createReadStream(path: string, options?: ReadStreamOptions): ReadStream; + export function createWriteStream(path: string, options?: WriteStreamOptions): WriteStream; + export function createOutputStream(path: string, options?: WriteStreamOptions): WriteStream; +} \ No newline at end of file diff --git a/typings/main/ambient/mime/mime.d.ts b/typings/main/ambient/mime/mime.d.ts new file mode 100644 index 00000000000..56903db17f1 --- /dev/null +++ b/typings/main/ambient/mime/mime.d.ts @@ -0,0 +1,22 @@ +// Compiled using typings@0.6.8 +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/cb5206a8ac1c9a3ddfd126f5ecea6729b2361452/mime/mime.d.ts +// Type definitions for mime +// Project: https://github.com/broofa/node-mime +// Definitions by: Jeff Goddard +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// Imported from: https://github.com/soywiz/typescript-node-definitions/mime.d.ts + +declare module "mime" { + export function lookup(path: string): string; + export function extension(mime: string): string; + export function load(filepath: string): void; + export function define(mimes: Object): void; + + interface Charsets { + lookup(mime: string): string; + } + + export var charsets: Charsets; + export var default_type: string; +} \ No newline at end of file diff --git a/typings/main/ambient/progress/progress.d.ts b/typings/main/ambient/progress/progress.d.ts new file mode 100644 index 00000000000..7ba74015197 --- /dev/null +++ b/typings/main/ambient/progress/progress.d.ts @@ -0,0 +1,122 @@ +// Compiled using typings@0.6.8 +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/d54b18e0ac3277376700b6026ef9e9e3f380df50/progress/progress.d.ts +// Type definitions for node-progress v1.1.8 +// Project: https://github.com/tj/node-progress +// Definitions by: Sebastian Lenz +// Definitions: https://github.com/borisyankov/DefinitelyTyped + + + +declare module "progress" +{ + /** + * These are keys in the options object you can pass to the progress bar along with total as seen in the example above. + */ + interface ProgressBarOptions + { + /** + * Total number of ticks to complete. + */ + total:number; + + /** + * The displayed width of the progress bar defaulting to total. + */ + width?:number; + + /** + * The output stream defaulting to stderr. + */ + stream?:NodeJS.WritableStream; + + /** + * Completion character defaulting to "=". + */ + complete?:string; + + /** + * Incomplete character defaulting to "-". + */ + incomplete?:string; + + /** + * Option to clear the bar on completion defaulting to false. + */ + clear?:boolean; + + /** + * Optional function to call when the progress bar completes. + */ + callback?:Function; + } + + + /** + * Flexible ascii progress bar. + */ + class ProgressBar + { + /** + * Initialize a `ProgressBar` with the given `fmt` string and `options` or + * `total`. + * + * Options: + * - `total` total number of ticks to complete + * - `width` the displayed width of the progress bar defaulting to total + * - `stream` the output stream defaulting to stderr + * - `complete` completion character defaulting to "=" + * - `incomplete` incomplete character defaulting to "-" + * - `renderThrottle` minimum time between updates in milliseconds defaulting to 16 + * - `callback` optional function to call when the progress bar completes + * - `clear` will clear the progress bar upon termination + * + * Tokens: + * - `:bar` the progress bar itself + * - `:current` current tick number + * - `:total` total ticks + * - `:elapsed` time elapsed in seconds + * - `:percent` completion percentage + * - `:eta` eta in seconds + */ + constructor(format:string, total:number); + constructor(format:string, options:ProgressBarOptions); + + + /** + * "tick" the progress bar with optional `len` and optional `tokens`. + */ + tick(tokens?:any):void; + tick(count?:number, tokens?:any):void; + + + /** + * Method to render the progress bar with optional `tokens` to place in the + * progress bar's `fmt` field. + */ + render(tokens?:any):void; + + + /** + * "update" the progress bar to represent an exact percentage. + * The ratio (between 0 and 1) specified will be multiplied by `total` and + * floored, representing the closest available "tick." For example, if a + * progress bar has a length of 3 and `update(0.5)` is called, the progress + * will be set to 1. + * + * A ratio of 0.5 will attempt to set the progress to halfway. + * + * @param ratio The ratio (between 0 and 1 inclusive) to set the + * overall completion to. + */ + update(ratio:number, tokens?:any):void; + + + /** + * Terminates a progress bar. + */ + terminate():void; + } + module ProgressBar { } + + export = ProgressBar; +} \ No newline at end of file diff --git a/typings/main/ambient/should/should.d.ts b/typings/main/ambient/should/should.d.ts new file mode 100644 index 00000000000..24795a8b3e8 --- /dev/null +++ b/typings/main/ambient/should/should.d.ts @@ -0,0 +1,172 @@ +// Compiled using typings@0.6.8 +// Source: https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/5aa6dff6990465c7fb41504538ad7aa133ab01d3/should/should.d.ts +// Type definitions for should.js v8.1.1 +// Project: https://github.com/shouldjs/should.js +// Definitions by: Alex Varju , Maxime LUCE +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface Object { + should: ShouldAssertion; +} + +interface ShouldAssertion { + // basic grammar + a: ShouldAssertion; + an: ShouldAssertion; + and: ShouldAssertion; + be: ShouldAssertion; + has: ShouldAssertion; + have: ShouldAssertion; + is: ShouldAssertion; + it: ShouldAssertion; + with: ShouldAssertion; + which: ShouldAssertion; + the: ShouldAssertion; + of: ShouldAssertion; + not: ShouldAssertion; + + // validators + arguments(): ShouldAssertion; + empty(): ShouldAssertion; + ok(): ShouldAssertion; + true(): ShouldAssertion; + false(): ShouldAssertion; + NaN(): ShouldAssertion; + Infinity(): ShouldAssertion; + Array(): ShouldAssertion; + Object(): ShouldAssertion; + String(): ShouldAssertion; + Boolean(): ShouldAssertion; + Number(): ShouldAssertion; + Error(): ShouldAssertion; + Function(): ShouldAssertion; + Date(): ShouldAssertion; + Class(): ShouldAssertion; + generator(): ShouldAssertion; + iterable(): ShouldAssertion; + iterator(): ShouldAssertion; + eql(expected: any, description?: string): ShouldAssertion; + equal(expected: any, description?: string): ShouldAssertion; + equalOneOf(...values: any[]): ShouldAssertion; + within(start: number, finish: number, description?: string): ShouldAssertion; + approximately(value: number, delta: number, description?: string): ShouldAssertion; + type(expected: any, description?: string): ShouldAssertion; + instanceof(constructor: Function, description?: string): ShouldAssertion; + above(n: number, description?: string): ShouldAssertion; + below(n: number, description?: string): ShouldAssertion; + aboveOrEqual(n: number, description?: string): ShouldAssertion; + greaterThanOrEqual(n: number, description?: string): ShouldAssertion; + belowOrEqual(n: number, description?: string): ShouldAssertion; + lessThanOrEqual(n: number, description?: string): ShouldAssertion; + match(other: {}, description?: string): ShouldAssertion; + match(other: (val: any) => any, description?: string): ShouldAssertion; + match(regexp: RegExp, description?: string): ShouldAssertion; + match(other: any, description?: string): ShouldAssertion; + matchEach(other: {}, description?: string): ShouldAssertion; + matchEach(other: (val: any) => any, description?: string): ShouldAssertion; + matchEach(regexp: RegExp, description?: string): ShouldAssertion; + matchEach(other: any, description?: string): ShouldAssertion; + matchAny(other: {}, description?: string): ShouldAssertion; + matchAny(other: (val: any) => any, description?: string): ShouldAssertion; + matchAny(regexp: RegExp, description?: string): ShouldAssertion; + matchAny(other: any, description?: string): ShouldAssertion; + length(n: number, description?: string): ShouldAssertion; + property(name: string, description?: string): ShouldAssertion; + property(name: string, val: any, description?: string): ShouldAssertion; + properties(names: string[]): ShouldAssertion; + properties(name: string): ShouldAssertion; + properties(descriptor: any): ShouldAssertion; + properties(...properties: string[]): ShouldAssertion; + propertyByPath(...properties: string[]): ShouldAssertion; + propertyWithDescriptor(name: string, descriptor: PropertyDescriptor): ShouldAssertion; + oneOf(...values: any[]): ShouldAssertion; + ownProperty(name: string, description?: string): ShouldAssertion; + containEql(obj: any): ShouldAssertion; + containDeep(obj: any): ShouldAssertion; + containDeepOrdered(obj: any): ShouldAssertion; + keys(...allKeys: string[]): ShouldAssertion; + keys(allKeys: string[]): ShouldAssertion; + enumerable(property: string, value?: any): ShouldAssertion; + enumerables(...properties: string[]): ShouldAssertion; + startWith(expected: string, message?: any): ShouldAssertion; + endWith(expected: string, message?: any): ShouldAssertion; + throw(message?: any): ShouldAssertion; + + //http + header(field: string, val?: string): ShouldAssertion; + status(code: number): ShouldAssertion; + json(): ShouldAssertion; + html(): ShouldAssertion; + + //stubs + alwaysCalledOn(thisTarget: any): ShouldAssertion; + alwaysCalledWith(...arguments: any[]): ShouldAssertion; + alwaysCalledWithExactly(...arguments: any[]): ShouldAssertion; + alwaysCalledWithMatch(...arguments: any[]): ShouldAssertion; + alwaysCalledWithNew(): ShouldAssertion; + alwaysThrew(exception?: any): ShouldAssertion; + callCount(count: number): ShouldAssertion; + called(): ShouldAssertion; + calledOn(thisTarget: any): ShouldAssertion; + calledOnce(): ShouldAssertion; + calledTwice(): ShouldAssertion; + calledThrice(): ShouldAssertion; + calledWith(...arguments: any[]): ShouldAssertion; + calledWithExactly(...arguments: any[]): ShouldAssertion; + calledWithMatch(...arguments: any[]): ShouldAssertion; + calledWithNew(): ShouldAssertion; + neverCalledWith(...arguments: any[]): ShouldAssertion; + neverCalledWithMatch(...arguments: any[]): ShouldAssertion; + threw(exception?: any): ShouldAssertion; + + // aliases + True(): ShouldAssertion; + False(): ShouldAssertion; + Arguments(): ShouldAssertion; + class(): ShouldAssertion; + deepEqual(expected: any, description?: string): ShouldAssertion; + exactly(expected: any, description?: string): ShouldAssertion; + instanceOf(constructor: Function, description?: string): ShouldAssertion; + throwError(message?: any): ShouldAssertion; + lengthOf(n: number, description?: string): ShouldAssertion; + key(key: string): ShouldAssertion; + hasOwnProperty(name: string, description?: string): ShouldAssertion; + greaterThan(n: number, description?: string): ShouldAssertion; + lessThan(n: number, description?: string): ShouldAssertion; +} + +interface ShouldInternal { + // should.js's extras + exist(actual: any, msg?: string): void; + exists(actual: any, msg?: string): void; + not: ShouldInternal; +} + +interface Internal extends ShouldInternal { + (obj: any): ShouldAssertion; + + // node.js's assert functions + fail(actual: any, expected: any, message: string, operator: string): void; + assert(value: any, message: string): void; + ok(value: any, message?: string): void; + equal(actual: any, expected: any, message?: string): void; + notEqual(actual: any, expected: any, message?: string): void; + deepEqual(actual: any, expected: any, message?: string): void; + notDeepEqual(actual: any, expected: any, message?: string): void; + strictEqual(actual: any, expected: any, message?: string): void; + notStrictEqual(actual: any, expected: any, message?: string): void; + throws(block: any, error?: any, message?: string): void; + doesNotThrow(block: any, message?: string): void; + ifError(value: any): void; + inspect(value: any, obj: any): any; +} + +declare var should: Internal; +declare var Should: Internal; +interface Window { + Should: Internal; +} + +declare module "should" { + export = should; +} \ No newline at end of file diff --git a/typings/main/definitions/source-map-support/source-map-support.d.ts b/typings/main/definitions/source-map-support/source-map-support.d.ts new file mode 100644 index 00000000000..adf8960c1d7 --- /dev/null +++ b/typings/main/definitions/source-map-support/source-map-support.d.ts @@ -0,0 +1,46 @@ +// Compiled using typings@0.6.8 +// Source: https://raw.githubusercontent.com/typed-typings/npm-source-map-support/900ed4180a22285bce4bbabc0760427e71a59eca/source-map-support.d.ts +declare module 'source-map-support/source-map-support' { +// Type definitions for source-map-support 0.2.10 +// Project: https://github.com/evanw/source-map-support +// Definitions by: Bart van der Schoor +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/** + * Output of retrieveSourceMap(). + */ +export interface UrlAndMap { + url: string; + map: string | Buffer; +} + +/** + * Options to install(). + */ +export interface Options { + handleUncaughtExceptions?: boolean; + emptyCacheBetweenOperations?: boolean; + retrieveFile?: (path: string) => string; + retrieveSourceMap?: (source: string) => UrlAndMap; +} + +export interface Position { + source: string; + line: number; + column: number; +} + +export function wrapCallSite (frame: any /* StackFrame */): any /* StackFrame */; +export function getErrorSource (error: Error): string; +export function mapSourcePosition (position: Position): Position; +export function retrieveSourceMap (source: string): UrlAndMap; + +/** + * Install SourceMap support. + * @param options Can be used to e.g. disable uncaughtException handler. + */ +export function install (options?: Options): void; +} +declare module 'source-map-support' { +export * from 'source-map-support/source-map-support'; +} \ No newline at end of file diff --git a/typings/node.d.ts b/typings/node.d.ts new file mode 100644 index 00000000000..b4047f0eb2c --- /dev/null +++ b/typings/node.d.ts @@ -0,0 +1,2179 @@ +// Type definitions for Node.js v4.x +// Project: http://nodejs.org/ +// Definitions by: Microsoft TypeScript , DefinitelyTyped +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/************************************************ +* * +* Node.js v4.x API * +* * +************************************************/ + +interface Error { + stack?: string; +} + + +// compat for TypeScript 1.5.3 +// if you use with --target es3 or --target es5 and use below definitions, +// use the lib.es6.d.ts that is bundled with TypeScript 1.5.3. +interface MapConstructor {} +interface WeakMapConstructor {} +interface SetConstructor {} +interface WeakSetConstructor {} + +/************************************************ +* * +* GLOBAL * +* * +************************************************/ +declare var process: NodeJS.Process; +declare var global: NodeJS.Global; + +declare var __filename: string; +declare var __dirname: string; + +declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; +declare function clearTimeout(timeoutId: NodeJS.Timer): void; +declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer; +declare function clearInterval(intervalId: NodeJS.Timer): void; +declare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; +declare function clearImmediate(immediateId: any): void; + +interface NodeRequireFunction { + (id: string): any; +} + +interface NodeRequire extends NodeRequireFunction { + resolve(id:string): string; + cache: any; + extensions: any; + main: any; +} + +declare var require: NodeRequire; + +interface NodeModule { + exports: any; + require: NodeRequireFunction; + id: string; + filename: string; + loaded: boolean; + parent: any; + children: any[]; +} + +declare var module: NodeModule; + +// Same as module.exports +declare var exports: any; +declare var SlowBuffer: { + new (str: string, encoding?: string): Buffer; + new (size: number): Buffer; + new (size: Uint8Array): Buffer; + new (array: any[]): Buffer; + prototype: Buffer; + isBuffer(obj: any): boolean; + byteLength(string: string, encoding?: string): number; + concat(list: Buffer[], totalLength?: number): Buffer; +}; + + +// Buffer class +interface Buffer extends NodeBuffer {} + +/** + * Raw data is stored in instances of the Buffer class. + * A Buffer is similar to an array of integers but corresponds to a raw memory allocation outside the V8 heap. A Buffer cannot be resized. + * Valid string encodings: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + */ +declare var Buffer: { + /** + * Allocates a new buffer containing the given {str}. + * + * @param str String to store in buffer. + * @param encoding encoding to use, optional. Default is 'utf8' + */ + new (str: string, encoding?: string): Buffer; + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + */ + new (size: number): Buffer; + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + new (array: Uint8Array): Buffer; + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + */ + new (array: any[]): Buffer; + /** + * Copies the passed {buffer} data onto a new {Buffer} instance. + * + * @param buffer The buffer to copy. + */ + new (buffer: Buffer): Buffer; + prototype: Buffer; + /** + * Returns true if {obj} is a Buffer + * + * @param obj object to test. + */ + isBuffer(obj: any): obj is Buffer; + /** + * Returns true if {encoding} is a valid encoding argument. + * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex' + * + * @param encoding string to test. + */ + isEncoding(encoding: string): boolean; + /** + * Gives the actual byte length of a string. encoding defaults to 'utf8'. + * This is not the same as String.prototype.length since that returns the number of characters in a string. + * + * @param string string to test. + * @param encoding encoding used to evaluate (defaults to 'utf8') + */ + byteLength(string: string, encoding?: string): number; + /** + * Returns a buffer which is the result of concatenating all the buffers in the list together. + * + * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer. + * If the list has exactly one item, then the first item of the list is returned. + * If the list has more than one item, then a new Buffer is created. + * + * @param list An array of Buffer objects to concatenate + * @param totalLength Total length of the buffers when concatenated. + * If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly. + */ + concat(list: Buffer[], totalLength?: number): Buffer; + /** + * The same as buf1.compare(buf2). + */ + compare(buf1: Buffer, buf2: Buffer): number; +}; + +/************************************************ +* * +* GLOBAL INTERFACES * +* * +************************************************/ +declare module NodeJS { + export interface ErrnoException extends Error { + errno?: number; + code?: string; + path?: string; + syscall?: string; + stack?: string; + } + + export interface EventEmitter { + addListener(event: string, listener: Function): EventEmitter; + on(event: string, listener: Function): EventEmitter; + once(event: string, listener: Function): EventEmitter; + removeListener(event: string, listener: Function): EventEmitter; + removeAllListeners(event?: string): EventEmitter; + setMaxListeners(n: number): EventEmitter; + getMaxListeners(): number; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + listenerCount(type: string): number; + } + + export interface ReadableStream extends EventEmitter { + readable: boolean; + read(size?: number): string|Buffer; + setEncoding(encoding: string): void; + pause(): void; + resume(): void; + pipe(destination: T, options?: { end?: boolean; }): T; + unpipe(destination?: T): void; + unshift(chunk: string): void; + unshift(chunk: Buffer): void; + wrap(oldStream: ReadableStream): ReadableStream; + } + + export interface WritableStream extends EventEmitter { + writable: boolean; + write(buffer: Buffer|string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + } + + export interface ReadWriteStream extends ReadableStream, WritableStream {} + + export interface Process extends EventEmitter { + stdout: WritableStream; + stderr: WritableStream; + stdin: ReadableStream; + argv: string[]; + execPath: string; + abort(): void; + chdir(directory: string): void; + cwd(): string; + env: any; + exit(code?: number): void; + getgid(): number; + setgid(id: number): void; + setgid(id: string): void; + getuid(): number; + setuid(id: number): void; + setuid(id: string): void; + version: string; + versions: { + http_parser: string; + node: string; + v8: string; + ares: string; + uv: string; + zlib: string; + openssl: string; + }; + config: { + target_defaults: { + cflags: any[]; + default_configuration: string; + defines: string[]; + include_dirs: string[]; + libraries: string[]; + }; + variables: { + clang: number; + host_arch: string; + node_install_npm: boolean; + node_install_waf: boolean; + node_prefix: string; + node_shared_openssl: boolean; + node_shared_v8: boolean; + node_shared_zlib: boolean; + node_use_dtrace: boolean; + node_use_etw: boolean; + node_use_openssl: boolean; + target_arch: string; + v8_no_strict_aliasing: number; + v8_use_snapshot: boolean; + visibility: string; + }; + }; + kill(pid:number, signal?: string|number): void; + pid: number; + title: string; + arch: string; + platform: string; + memoryUsage(): { rss: number; heapTotal: number; heapUsed: number; }; + nextTick(callback: Function): void; + umask(mask?: number): number; + uptime(): number; + hrtime(time?:number[]): number[]; + + // Worker + send?(message: any, sendHandle?: any): void; + } + + export interface Global { + Array: typeof Array; + ArrayBuffer: typeof ArrayBuffer; + Boolean: typeof Boolean; + Buffer: typeof Buffer; + DataView: typeof DataView; + Date: typeof Date; + Error: typeof Error; + EvalError: typeof EvalError; + Float32Array: typeof Float32Array; + Float64Array: typeof Float64Array; + Function: typeof Function; + GLOBAL: Global; + Infinity: typeof Infinity; + Int16Array: typeof Int16Array; + Int32Array: typeof Int32Array; + Int8Array: typeof Int8Array; + Intl: typeof Intl; + JSON: typeof JSON; + Map: MapConstructor; + Math: typeof Math; + NaN: typeof NaN; + Number: typeof Number; + Object: typeof Object; + Promise: Function; + RangeError: typeof RangeError; + ReferenceError: typeof ReferenceError; + RegExp: typeof RegExp; + Set: SetConstructor; + String: typeof String; + Symbol: Function; + SyntaxError: typeof SyntaxError; + TypeError: typeof TypeError; + URIError: typeof URIError; + Uint16Array: typeof Uint16Array; + Uint32Array: typeof Uint32Array; + Uint8Array: typeof Uint8Array; + Uint8ClampedArray: Function; + WeakMap: WeakMapConstructor; + WeakSet: WeakSetConstructor; + clearImmediate: (immediateId: any) => void; + clearInterval: (intervalId: NodeJS.Timer) => void; + clearTimeout: (timeoutId: NodeJS.Timer) => void; + console: typeof console; + decodeURI: typeof decodeURI; + decodeURIComponent: typeof decodeURIComponent; + encodeURI: typeof encodeURI; + encodeURIComponent: typeof encodeURIComponent; + escape: (str: string) => string; + eval: typeof eval; + global: Global; + isFinite: typeof isFinite; + isNaN: typeof isNaN; + parseFloat: typeof parseFloat; + parseInt: typeof parseInt; + process: Process; + root: Global; + setImmediate: (callback: (...args: any[]) => void, ...args: any[]) => any; + setInterval: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + setTimeout: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer; + undefined: typeof undefined; + unescape: (str: string) => string; + gc: () => void; + v8debug?: any; + } + + export interface Timer { + ref() : void; + unref() : void; + } +} + +/** + * @deprecated + */ +interface NodeBuffer { + [index: number]: number; + write(string: string, offset?: number, length?: number, encoding?: string): number; + toString(encoding?: string, start?: number, end?: number): string; + toJSON(): any; + length: number; + equals(otherBuffer: Buffer): boolean; + compare(otherBuffer: Buffer): number; + copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(start?: number, end?: number): Buffer; + writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number; + readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntLE(offset: number, byteLength: number, noAssert?: boolean): number; + readIntBE(offset: number, byteLength: number, noAssert?: boolean): number; + readUInt8(offset: number, noAsset?: boolean): number; + readUInt16LE(offset: number, noAssert?: boolean): number; + readUInt16BE(offset: number, noAssert?: boolean): number; + readUInt32LE(offset: number, noAssert?: boolean): number; + readUInt32BE(offset: number, noAssert?: boolean): number; + readInt8(offset: number, noAssert?: boolean): number; + readInt16LE(offset: number, noAssert?: boolean): number; + readInt16BE(offset: number, noAssert?: boolean): number; + readInt32LE(offset: number, noAssert?: boolean): number; + readInt32BE(offset: number, noAssert?: boolean): number; + readFloatLE(offset: number, noAssert?: boolean): number; + readFloatBE(offset: number, noAssert?: boolean): number; + readDoubleLE(offset: number, noAssert?: boolean): number; + readDoubleBE(offset: number, noAssert?: boolean): number; + writeUInt8(value: number, offset: number, noAssert?: boolean): number; + writeUInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeUInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeInt8(value: number, offset: number, noAssert?: boolean): number; + writeInt16LE(value: number, offset: number, noAssert?: boolean): number; + writeInt16BE(value: number, offset: number, noAssert?: boolean): number; + writeInt32LE(value: number, offset: number, noAssert?: boolean): number; + writeInt32BE(value: number, offset: number, noAssert?: boolean): number; + writeFloatLE(value: number, offset: number, noAssert?: boolean): number; + writeFloatBE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleLE(value: number, offset: number, noAssert?: boolean): number; + writeDoubleBE(value: number, offset: number, noAssert?: boolean): number; + fill(value: any, offset?: number, end?: number): Buffer; + indexOf(value: string | number | Buffer, byteOffset?: number): number; +} + +/************************************************ +* * +* MODULES * +* * +************************************************/ +declare module "buffer" { + export var INSPECT_MAX_BYTES: number; +} + +declare module "querystring" { + export interface StringifyOptions { + encodeURIComponent?: Function; + } + + export interface ParseOptions { + maxKeys?: number; + decodeURIComponent?: Function; + } + + export function stringify(obj: T, sep?: string, eq?: string, options?: StringifyOptions): string; + export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): any; + export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): T; + export function escape(str: string): string; + export function unescape(str: string): string; +} + +declare module "events" { + export class EventEmitter implements NodeJS.EventEmitter { + static EventEmitter: EventEmitter; + static listenerCount(emitter: EventEmitter, event: string): number; // deprecated + static defaultMaxListeners: number; + + addListener(event: string, listener: Function): EventEmitter; + on(event: string, listener: Function): EventEmitter; + once(event: string, listener: Function): EventEmitter; + removeListener(event: string, listener: Function): EventEmitter; + removeAllListeners(event?: string): EventEmitter; + setMaxListeners(n: number): EventEmitter; + getMaxListeners(): number; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + listenerCount(type: string): number; + } +} + +declare module "http" { + import * as events from "events"; + import * as net from "net"; + import * as stream from "stream"; + + export interface RequestOptions { + protocol?: string; + host?: string; + hostname?: string; + family?: number; + port?: number; + localAddress?: string; + socketPath?: string; + method?: string; + path?: string; + headers?: { [key: string]: any }; + auth?: string; + agent?: Agent|boolean; + } + + export interface Server extends events.EventEmitter { + listen(port: number, hostname?: string, backlog?: number, callback?: Function): Server; + listen(port: number, hostname?: string, callback?: Function): Server; + listen(path: string, callback?: Function): Server; + listen(handle: any, listeningListener?: Function): Server; + close(cb?: any): Server; + address(): { port: number; family: string; address: string; }; + maxHeadersCount: number; + } + /** + * @deprecated Use IncomingMessage + */ + export interface ServerRequest extends IncomingMessage { + connection: net.Socket; + } + export interface ServerResponse extends events.EventEmitter, stream.Writable { + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + + writeContinue(): void; + writeHead(statusCode: number, reasonPhrase?: string, headers?: any): void; + writeHead(statusCode: number, headers?: any): void; + statusCode: number; + statusMessage: string; + headersSent: boolean; + setHeader(name: string, value: string): void; + sendDate: boolean; + getHeader(name: string): string; + removeHeader(name: string): void; + write(chunk: any, encoding?: string): any; + addTrailers(headers: any): void; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; + } + export interface ClientRequest extends events.EventEmitter, stream.Writable { + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + + write(chunk: any, encoding?: string): void; + abort(): void; + setTimeout(timeout: number, callback?: Function): void; + setNoDelay(noDelay?: boolean): void; + setSocketKeepAlive(enable?: boolean, initialDelay?: number): void; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; + } + export interface IncomingMessage extends events.EventEmitter, stream.Readable { + httpVersion: string; + headers: any; + rawHeaders: string[]; + trailers: any; + rawTrailers: any; + setTimeout(msecs: number, callback: Function): NodeJS.Timer; + /** + * Only valid for request obtained from http.Server. + */ + method?: string; + /** + * Only valid for request obtained from http.Server. + */ + url?: string; + /** + * Only valid for response obtained from http.ClientRequest. + */ + statusCode?: number; + /** + * Only valid for response obtained from http.ClientRequest. + */ + statusMessage?: string; + socket: net.Socket; + } + /** + * @deprecated Use IncomingMessage + */ + export interface ClientResponse extends IncomingMessage { } + + export interface AgentOptions { + /** + * Keep sockets around in a pool to be used by other requests in the future. Default = false + */ + keepAlive?: boolean; + /** + * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000. + * Only relevant if keepAlive is set to true. + */ + keepAliveMsecs?: number; + /** + * Maximum number of sockets to allow per host. Default for Node 0.10 is 5, default for Node 0.12 is Infinity + */ + maxSockets?: number; + /** + * Maximum number of sockets to leave open in a free state. Only relevant if keepAlive is set to true. Default = 256. + */ + maxFreeSockets?: number; + } + + export class Agent { + maxSockets: number; + sockets: any; + requests: any; + + constructor(opts?: AgentOptions); + + /** + * Destroy any sockets that are currently in use by the agent. + * It is usually not necessary to do this. However, if you are using an agent with KeepAlive enabled, + * then it is best to explicitly shut down the agent when you know that it will no longer be used. Otherwise, + * sockets may hang open for quite a long time before the server terminates them. + */ + destroy(): void; + } + + export var METHODS: string[]; + + export var STATUS_CODES: { + [errorCode: number]: string; + [errorCode: string]: string; + }; + export function createServer(requestListener?: (request: IncomingMessage, response: ServerResponse) =>void ): Server; + export function createClient(port?: number, host?: string): any; + export function request(options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest; + export function get(options: any, callback?: (res: IncomingMessage) => void): ClientRequest; + export var globalAgent: Agent; +} + +declare module "cluster" { + import * as child from "child_process"; + import * as events from "events"; + + export interface ClusterSettings { + exec?: string; + args?: string[]; + silent?: boolean; + } + + export class Worker extends events.EventEmitter { + id: string; + process: child.ChildProcess; + suicide: boolean; + send(message: any, sendHandle?: any): void; + kill(signal?: string): void; + destroy(signal?: string): void; + disconnect(): void; + } + + export var settings: ClusterSettings; + export var isMaster: boolean; + export var isWorker: boolean; + export function setupMaster(settings?: ClusterSettings): void; + export function fork(env?: any): Worker; + export function disconnect(callback?: Function): void; + export var worker: Worker; + export var workers: Worker[]; + + // Event emitter + export function addListener(event: string, listener: Function): void; + export function on(event: "disconnect", listener: (worker: Worker) => void): void; + export function on(event: "exit", listener: (worker: Worker, code: number, signal: string) => void): void; + export function on(event: "fork", listener: (worker: Worker) => void): void; + export function on(event: "listening", listener: (worker: Worker, address: any) => void): void; + export function on(event: "message", listener: (worker: Worker, message: any) => void): void; + export function on(event: "online", listener: (worker: Worker) => void): void; + export function on(event: "setup", listener: (settings: any) => void): void; + export function on(event: string, listener: Function): any; + export function once(event: string, listener: Function): void; + export function removeListener(event: string, listener: Function): void; + export function removeAllListeners(event?: string): void; + export function setMaxListeners(n: number): void; + export function listeners(event: string): Function[]; + export function emit(event: string, ...args: any[]): boolean; +} + +declare module "zlib" { + import * as stream from "stream"; + export interface ZlibOptions { chunkSize?: number; windowBits?: number; level?: number; memLevel?: number; strategy?: number; dictionary?: any; } + + export interface Gzip extends stream.Transform { } + export interface Gunzip extends stream.Transform { } + export interface Deflate extends stream.Transform { } + export interface Inflate extends stream.Transform { } + export interface DeflateRaw extends stream.Transform { } + export interface InflateRaw extends stream.Transform { } + export interface Unzip extends stream.Transform { } + + export function createGzip(options?: ZlibOptions): Gzip; + export function createGunzip(options?: ZlibOptions): Gunzip; + export function createDeflate(options?: ZlibOptions): Deflate; + export function createInflate(options?: ZlibOptions): Inflate; + export function createDeflateRaw(options?: ZlibOptions): DeflateRaw; + export function createInflateRaw(options?: ZlibOptions): InflateRaw; + export function createUnzip(options?: ZlibOptions): Unzip; + + export function deflate(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function deflateSync(buf: Buffer, options?: ZlibOptions): any; + export function deflateRaw(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function deflateRawSync(buf: Buffer, options?: ZlibOptions): any; + export function gzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function gzipSync(buf: Buffer, options?: ZlibOptions): any; + export function gunzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function gunzipSync(buf: Buffer, options?: ZlibOptions): any; + export function inflate(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function inflateSync(buf: Buffer, options?: ZlibOptions): any; + export function inflateRaw(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function inflateRawSync(buf: Buffer, options?: ZlibOptions): any; + export function unzip(buf: Buffer, callback: (error: Error, result: any) =>void ): void; + export function unzipSync(buf: Buffer, options?: ZlibOptions): any; + + // Constants + export var Z_NO_FLUSH: number; + export var Z_PARTIAL_FLUSH: number; + export var Z_SYNC_FLUSH: number; + export var Z_FULL_FLUSH: number; + export var Z_FINISH: number; + export var Z_BLOCK: number; + export var Z_TREES: number; + export var Z_OK: number; + export var Z_STREAM_END: number; + export var Z_NEED_DICT: number; + export var Z_ERRNO: number; + export var Z_STREAM_ERROR: number; + export var Z_DATA_ERROR: number; + export var Z_MEM_ERROR: number; + export var Z_BUF_ERROR: number; + export var Z_VERSION_ERROR: number; + export var Z_NO_COMPRESSION: number; + export var Z_BEST_SPEED: number; + export var Z_BEST_COMPRESSION: number; + export var Z_DEFAULT_COMPRESSION: number; + export var Z_FILTERED: number; + export var Z_HUFFMAN_ONLY: number; + export var Z_RLE: number; + export var Z_FIXED: number; + export var Z_DEFAULT_STRATEGY: number; + export var Z_BINARY: number; + export var Z_TEXT: number; + export var Z_ASCII: number; + export var Z_UNKNOWN: number; + export var Z_DEFLATED: number; + export var Z_NULL: number; +} + +declare module "os" { + export interface CpuInfo { + model: string; + speed: number; + times: { + user: number; + nice: number; + sys: number; + idle: number; + irq: number; + }; + } + + export interface NetworkInterfaceInfo { + address: string; + netmask: string; + family: string; + mac: string; + internal: boolean; + } + + export function tmpdir(): string; + export function homedir(): string; + export function endianness(): string; + export function hostname(): string; + export function type(): string; + export function platform(): string; + export function arch(): string; + export function release(): string; + export function uptime(): number; + export function loadavg(): number[]; + export function totalmem(): number; + export function freemem(): number; + export function cpus(): CpuInfo[]; + export function networkInterfaces(): {[index: string]: NetworkInterfaceInfo[]}; + export var EOL: string; +} + +declare module "https" { + import * as tls from "tls"; + import * as events from "events"; + import * as http from "http"; + + export interface ServerOptions { + pfx?: any; + key?: any; + passphrase?: string; + cert?: any; + ca?: any; + crl?: any; + ciphers?: string; + honorCipherOrder?: boolean; + requestCert?: boolean; + rejectUnauthorized?: boolean; + NPNProtocols?: any; + SNICallback?: (servername: string) => any; + } + + export interface RequestOptions extends http.RequestOptions{ + pfx?: any; + key?: any; + passphrase?: string; + cert?: any; + ca?: any; + ciphers?: string; + rejectUnauthorized?: boolean; + secureProtocol?: string; + } + + export interface Agent { + maxSockets: number; + sockets: any; + requests: any; + } + export var Agent: { + new (options?: RequestOptions): Agent; + }; + export interface Server extends tls.Server { } + export function createServer(options: ServerOptions, requestListener?: Function): Server; + export function request(options: RequestOptions, callback?: (res: http.IncomingMessage) =>void ): http.ClientRequest; + export function get(options: RequestOptions, callback?: (res: http.IncomingMessage) =>void ): http.ClientRequest; + export var globalAgent: Agent; +} + +declare module "punycode" { + export function decode(string: string): string; + export function encode(string: string): string; + export function toUnicode(domain: string): string; + export function toASCII(domain: string): string; + export var ucs2: ucs2; + interface ucs2 { + decode(string: string): number[]; + encode(codePoints: number[]): string; + } + export var version: any; +} + +declare module "repl" { + import * as stream from "stream"; + import * as events from "events"; + + export interface ReplOptions { + prompt?: string; + input?: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + terminal?: boolean; + eval?: Function; + useColors?: boolean; + useGlobal?: boolean; + ignoreUndefined?: boolean; + writer?: Function; + } + export function start(options: ReplOptions): events.EventEmitter; +} + +declare module "readline" { + import * as events from "events"; + import * as stream from "stream"; + + export interface Key { + sequence?: string; + name?: string; + ctrl?: boolean; + meta?: boolean; + shift?: boolean; + } + + export interface ReadLine extends events.EventEmitter { + setPrompt(prompt: string): void; + prompt(preserveCursor?: boolean): void; + question(query: string, callback: (answer: string) => void): void; + pause(): ReadLine; + resume(): ReadLine; + close(): void; + write(data: string|Buffer, key?: Key): void; + } + + export interface Completer { + (line: string): CompleterResult; + (line: string, callback: (err: any, result: CompleterResult) => void): any; + } + + export interface CompleterResult { + completions: string[]; + line: string; + } + + export interface ReadLineOptions { + input: NodeJS.ReadableStream; + output?: NodeJS.WritableStream; + completer?: Completer; + terminal?: boolean; + historySize?: number; + } + + export function createInterface(input: NodeJS.ReadableStream, output?: NodeJS.WritableStream, completer?: Completer, terminal?: boolean): ReadLine; + export function createInterface(options: ReadLineOptions): ReadLine; + + export function cursorTo(stream: NodeJS.WritableStream, x: number, y: number): void; + export function moveCursor(stream: NodeJS.WritableStream, dx: number|string, dy: number|string): void; + export function clearLine(stream: NodeJS.WritableStream, dir: number): void; + export function clearScreenDown(stream: NodeJS.WritableStream): void; +} + +declare module "vm" { + export interface Context { } + export interface Script { + runInThisContext(): void; + runInNewContext(sandbox?: Context): void; + } + export function runInThisContext(code: string, filename?: string): void; + export function runInNewContext(code: string, sandbox?: Context, filename?: string): void; + export function runInContext(code: string, context: Context, filename?: string): void; + export function createContext(initSandbox?: Context): Context; + export function createScript(code: string, filename?: string): Script; +} + +declare module "child_process" { + import * as events from "events"; + import * as stream from "stream"; + + export interface ChildProcess extends events.EventEmitter { + stdin: stream.Writable; + stdout: stream.Readable; + stderr: stream.Readable; + pid: number; + kill(signal?: string): void; + send(message: any, sendHandle?: any): void; + disconnect(): void; + unref(): void; + } + + export function spawn(command: string, args?: string[], options?: { + cwd?: string; + stdio?: any; + custom?: any; + env?: any; + detached?: boolean; + }): ChildProcess; + export function exec(command: string, options: { + cwd?: string; + stdio?: any; + customFds?: any; + env?: any; + encoding?: string; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + }, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function exec(command: string, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function execFile(file: string, + callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function execFile(file: string, args?: string[], + callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function execFile(file: string, args?: string[], options?: { + cwd?: string; + stdio?: any; + customFds?: any; + env?: any; + encoding?: string; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + }, callback?: (error: Error, stdout: Buffer, stderr: Buffer) =>void ): ChildProcess; + export function fork(modulePath: string, args?: string[], options?: { + cwd?: string; + env?: any; + execPath?: string; + execArgv?: string[]; + silent?: boolean; + uid?: number; + gid?: number; + }): ChildProcess; + export function spawnSync(command: string, args?: string[], options?: { + cwd?: string; + input?: string | Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + encoding?: string; + }): { + pid: number; + output: string[]; + stdout: string | Buffer; + stderr: string | Buffer; + status: number; + signal: string; + error: Error; + }; + export function execSync(command: string, options?: { + cwd?: string; + input?: string|Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + encoding?: string; + }): string | Buffer; + export function execFileSync(command: string, args?: string[], options?: { + cwd?: string; + input?: string|Buffer; + stdio?: any; + env?: any; + uid?: number; + gid?: number; + timeout?: number; + maxBuffer?: number; + killSignal?: string; + encoding?: string; + }): string | Buffer; +} + +declare module "url" { + export interface Url { + href?: string; + protocol?: string; + auth?: string; + hostname?: string; + port?: string; + host?: string; + pathname?: string; + search?: string; + query?: any; // string | Object + slashes?: boolean; + hash?: string; + path?: string; + } + + export function parse(urlStr: string, parseQueryString?: boolean , slashesDenoteHost?: boolean ): Url; + export function format(url: Url): string; + export function resolve(from: string, to: string): string; +} + +declare module "dns" { + export function lookup(domain: string, family: number, callback: (err: Error, address: string, family: number) =>void ): string; + export function lookup(domain: string, callback: (err: Error, address: string, family: number) =>void ): string; + export function resolve(domain: string, rrtype: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolve(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolve4(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolve6(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveMx(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveTxt(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveSrv(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveNs(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function resolveCname(domain: string, callback: (err: Error, addresses: string[]) =>void ): string[]; + export function reverse(ip: string, callback: (err: Error, domains: string[]) =>void ): string[]; +} + +declare module "net" { + import * as stream from "stream"; + + export interface Socket extends stream.Duplex { + // Extended base methods + write(buffer: Buffer): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: string, cb?: Function): boolean; + write(str: string, encoding?: string, cb?: Function): boolean; + write(str: string, encoding?: string, fd?: string): boolean; + + connect(port: number, host?: string, connectionListener?: Function): void; + connect(path: string, connectionListener?: Function): void; + bufferSize: number; + setEncoding(encoding?: string): void; + write(data: any, encoding?: string, callback?: Function): void; + destroy(): void; + pause(): void; + resume(): void; + setTimeout(timeout: number, callback?: Function): void; + setNoDelay(noDelay?: boolean): void; + setKeepAlive(enable?: boolean, initialDelay?: number): void; + address(): { port: number; family: string; address: string; }; + unref(): void; + ref(): void; + + remoteAddress: string; + remoteFamily: string; + remotePort: number; + localAddress: string; + localPort: number; + bytesRead: number; + bytesWritten: number; + + // Extended base methods + end(): void; + end(buffer: Buffer, cb?: Function): void; + end(str: string, cb?: Function): void; + end(str: string, encoding?: string, cb?: Function): void; + end(data?: any, encoding?: string): void; + } + + export var Socket: { + new (options?: { fd?: string; type?: string; allowHalfOpen?: boolean; }): Socket; + }; + + export interface Server extends Socket { + listen(port: number, host?: string, backlog?: number, listeningListener?: Function): Server; + listen(path: string, listeningListener?: Function): Server; + listen(handle: any, listeningListener?: Function): Server; + close(callback?: Function): Server; + address(): { port: number; family: string; address: string; }; + maxConnections: number; + connections: number; + } + export function createServer(connectionListener?: (socket: Socket) =>void ): Server; + export function createServer(options?: { allowHalfOpen?: boolean; }, connectionListener?: (socket: Socket) =>void ): Server; + export function connect(options: { port: number, host?: string, localAddress? : string, localPort? : string, family? : number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; + export function connect(port: number, host?: string, connectionListener?: Function): Socket; + export function connect(path: string, connectionListener?: Function): Socket; + export function createConnection(options: { port: number, host?: string, localAddress? : string, localPort? : string, family? : number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket; + export function createConnection(port: number, host?: string, connectionListener?: Function): Socket; + export function createConnection(path: string, connectionListener?: Function): Socket; + export function isIP(input: string): number; + export function isIPv4(input: string): boolean; + export function isIPv6(input: string): boolean; +} + +declare module "dgram" { + import * as events from "events"; + + interface RemoteInfo { + address: string; + port: number; + size: number; + } + + interface AddressInfo { + address: string; + family: string; + port: number; + } + + export function createSocket(type: string, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket; + + interface Socket extends events.EventEmitter { + send(buf: Buffer, offset: number, length: number, port: number, address: string, callback?: (error: Error, bytes: number) => void): void; + bind(port: number, address?: string, callback?: () => void): void; + close(): void; + address(): AddressInfo; + setBroadcast(flag: boolean): void; + setMulticastTTL(ttl: number): void; + setMulticastLoopback(flag: boolean): void; + addMembership(multicastAddress: string, multicastInterface?: string): void; + dropMembership(multicastAddress: string, multicastInterface?: string): void; + } +} + +declare module "fs" { + import * as stream from "stream"; + import * as events from "events"; + + interface Stats { + isFile(): boolean; + isDirectory(): boolean; + isBlockDevice(): boolean; + isCharacterDevice(): boolean; + isSymbolicLink(): boolean; + isFIFO(): boolean; + isSocket(): boolean; + dev: number; + ino: number; + mode: number; + nlink: number; + uid: number; + gid: number; + rdev: number; + size: number; + blksize: number; + blocks: number; + atime: Date; + mtime: Date; + ctime: Date; + birthtime: Date; + } + + interface FSWatcher extends events.EventEmitter { + close(): void; + } + + export interface ReadStream extends stream.Readable { + close(): void; + } + export interface WriteStream extends stream.Writable { + close(cb: Function): void; + bytesWritten: number; + } + + /** + * Asynchronous rename. + * @param oldPath + * @param newPath + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /** + * Synchronous rename + * @param oldPath + * @param newPath + */ + export function renameSync(oldPath: string, newPath: string): void; + export function truncate(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function truncate(path: string, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function truncateSync(path: string, len?: number): void; + export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function ftruncateSync(fd: number, len?: number): void; + export function chown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chownSync(path: string, uid: number, gid: number): void; + export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fchownSync(fd: number, uid: number, gid: number): void; + export function lchown(path: string, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function lchownSync(path: string, uid: number, gid: number): void; + export function chmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function chmodSync(path: string, mode: number): void; + export function chmodSync(path: string, mode: string): void; + export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fchmodSync(fd: number, mode: number): void; + export function fchmodSync(fd: number, mode: string): void; + export function lchmod(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function lchmod(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function lchmodSync(path: string, mode: number): void; + export function lchmodSync(path: string, mode: string): void; + export function stat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; + export function lstat(path: string, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; + export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void; + export function statSync(path: string): Stats; + export function lstatSync(path: string): Stats; + export function fstatSync(fd: number): Stats; + export function link(srcpath: string, dstpath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function linkSync(srcpath: string, dstpath: string): void; + export function symlink(srcpath: string, dstpath: string, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function symlinkSync(srcpath: string, dstpath: string, type?: string): void; + export function readlink(path: string, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void; + export function readlinkSync(path: string): string; + export function realpath(path: string, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void; + export function realpath(path: string, cache: {[path: string]: string}, callback: (err: NodeJS.ErrnoException, resolvedPath: string) =>any): void; + export function realpathSync(path: string, cache?: { [path: string]: string }): string; + /* + * Asynchronous unlink - deletes the file specified in {path} + * + * @param path + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function unlink(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Synchronous unlink - deletes the file specified in {path} + * + * @param path + */ + export function unlinkSync(path: string): void; + /* + * Asynchronous rmdir - removes the directory specified in {path} + * + * @param path + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function rmdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Synchronous rmdir - removes the directory specified in {path} + * + * @param path + */ + export function rmdirSync(path: string): void; + /* + * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdir(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Asynchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdir(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + /* + * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdirSync(path: string, mode?: number): void; + /* + * Synchronous mkdir - creates the directory specified in {path}. Parameter {mode} defaults to 0777. + * + * @param path + * @param mode + * @param callback No arguments other than a possible exception are given to the completion callback. + */ + export function mkdirSync(path: string, mode?: string): void; + export function readdir(path: string, callback?: (err: NodeJS.ErrnoException, files: string[]) => void): void; + export function readdirSync(path: string): string[]; + export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function closeSync(fd: number): void; + export function open(path: string, flags: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; + export function open(path: string, flags: string, mode: number, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; + export function open(path: string, flags: string, mode: string, callback?: (err: NodeJS.ErrnoException, fd: number) => any): void; + export function openSync(path: string, flags: string, mode?: number): number; + export function openSync(path: string, flags: string, mode?: string): number; + export function utimes(path: string, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function utimes(path: string, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function utimesSync(path: string, atime: number, mtime: number): void; + export function utimesSync(path: string, atime: Date, mtime: Date): void; + export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function futimesSync(fd: number, atime: number, mtime: number): void; + export function futimesSync(fd: number, atime: Date, mtime: Date): void; + export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function fsyncSync(fd: number): void; + export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; + export function write(fd: number, buffer: Buffer, offset: number, length: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void; + export function write(fd: number, data: any, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + export function write(fd: number, data: any, offset: number, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + export function write(fd: number, data: any, offset: number, encoding: string, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void; + export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number; + export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void; + export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param encoding + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer. + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + /* + * Asynchronous readFile - Asynchronously reads the entire contents of a file. + * + * @param fileName + * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file. + */ + export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + /* + * Synchronous readFile - Synchronously reads the entire contents of a file. + * + * @param fileName + * @param encoding + */ + export function readFileSync(filename: string, encoding: string): string; + /* + * Synchronous readFile - Synchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. + */ + export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string; + /* + * Synchronous readFile - Synchronously reads the entire contents of a file. + * + * @param fileName + * @param options An object with optional {encoding} and {flag} properties. If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer. + */ + export function readFileSync(filename: string, options?: { flag?: string; }): Buffer; + + export function writeFile(filename: string, data: string | Buffer, callback?: (err: NodeJS.ErrnoException) => void): void; + export function writeFile(filename: string, data: string | Buffer, options: { encoding?: string; mode?: number | string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + + export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; + export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; + export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; + export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void; + export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void; + export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void; + export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void; + export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void; + export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher; + export function watch(filename: string, options: { persistent?: boolean; }, listener?: (event: string, filename: string) => any): FSWatcher; + export function exists(path: string, callback?: (exists: boolean) => void): void; + export function existsSync(path: string): boolean; + /** Constant for fs.access(). File is visible to the calling process. */ + export var F_OK: number; + /** Constant for fs.access(). File can be read by the calling process. */ + export var R_OK: number; + /** Constant for fs.access(). File can be written by the calling process. */ + export var W_OK: number; + /** Constant for fs.access(). File can be executed by the calling process. */ + export var X_OK: number; + /** Tests a user's permissions for the file specified by path. */ + export function access(path: string, callback: (err: NodeJS.ErrnoException) => void): void; + export function access(path: string, mode: number, callback: (err: NodeJS.ErrnoException) => void): void; + /** Synchronous version of fs.access. This throws if any accessibility checks fail, and does nothing otherwise. */ + export function accessSync(path: string, mode ?: number): void; + export function createReadStream(path: string, options?: { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + autoClose?: boolean; + }): ReadStream; + export function createWriteStream(path: string, options?: { + flags?: string; + encoding?: string; + fd?: number; + mode?: number; + }): WriteStream; +} + +declare module "path" { + + /** + * A parsed path object generated by path.parse() or consumed by path.format(). + */ + export interface ParsedPath { + /** + * The root of the path such as '/' or 'c:\' + */ + root: string; + /** + * The full directory path such as '/home/user/dir' or 'c:\path\dir' + */ + dir: string; + /** + * The file name including extension (if any) such as 'index.html' + */ + base: string; + /** + * The file extension (if any) such as '.html' + */ + ext: string; + /** + * The file name without extension (if any) such as 'index' + */ + name: string; + } + + /** + * Normalize a string path, reducing '..' and '.' parts. + * When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved. On Windows backslashes are used. + * + * @param p string path to normalize. + */ + export function normalize(p: string): string; + /** + * Join all arguments together and normalize the resulting path. + * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. + * + * @param paths string paths to join. + */ + export function join(...paths: any[]): string; + /** + * Join all arguments together and normalize the resulting path. + * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown. + * + * @param paths string paths to join. + */ + export function join(...paths: string[]): string; + /** + * The right-most parameter is considered {to}. Other parameters are considered an array of {from}. + * + * Starting from leftmost {from} paramter, resolves {to} to an absolute path. + * + * If {to} isn't already absolute, {from} arguments are prepended in right to left order, until an absolute path is found. If after using all {from} paths still no absolute path is found, the current working directory is used as well. The resulting path is normalized, and trailing slashes are removed unless the path gets resolved to the root directory. + * + * @param pathSegments string paths to join. Non-string arguments are ignored. + */ + export function resolve(...pathSegments: any[]): string; + /** + * Determines whether {path} is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory. + * + * @param path path to test. + */ + export function isAbsolute(path: string): boolean; + /** + * Solve the relative path from {from} to {to}. + * At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve. + * + * @param from + * @param to + */ + export function relative(from: string, to: string): string; + /** + * Return the directory name of a path. Similar to the Unix dirname command. + * + * @param p the path to evaluate. + */ + export function dirname(p: string): string; + /** + * Return the last portion of a path. Similar to the Unix basename command. + * Often used to extract the file name from a fully qualified path. + * + * @param p the path to evaluate. + * @param ext optionally, an extension to remove from the result. + */ + export function basename(p: string, ext?: string): string; + /** + * Return the extension of the path, from the last '.' to end of string in the last portion of the path. + * If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string + * + * @param p the path to evaluate. + */ + export function extname(p: string): string; + /** + * The platform-specific file separator. '\\' or '/'. + */ + export var sep: string; + /** + * The platform-specific file delimiter. ';' or ':'. + */ + export var delimiter: string; + /** + * Returns an object from a path string - the opposite of format(). + * + * @param pathString path to evaluate. + */ + export function parse(pathString: string): ParsedPath; + /** + * Returns a path string from an object - the opposite of parse(). + * + * @param pathString path to evaluate. + */ + export function format(pathObject: ParsedPath): string; + + export module posix { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + export function isAbsolute(p: string): boolean; + export function relative(from: string, to: string): string; + export function dirname(p: string): string; + export function basename(p: string, ext?: string): string; + export function extname(p: string): string; + export var sep: string; + export var delimiter: string; + export function parse(p: string): ParsedPath; + export function format(pP: ParsedPath): string; + } + + export module win32 { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + export function isAbsolute(p: string): boolean; + export function relative(from: string, to: string): string; + export function dirname(p: string): string; + export function basename(p: string, ext?: string): string; + export function extname(p: string): string; + export var sep: string; + export var delimiter: string; + export function parse(p: string): ParsedPath; + export function format(pP: ParsedPath): string; + } +} + +declare module "string_decoder" { + export interface NodeStringDecoder { + write(buffer: Buffer): string; + detectIncompleteChar(buffer: Buffer): number; + } + export var StringDecoder: { + new (encoding: string): NodeStringDecoder; + }; +} + +declare module "tls" { + import * as crypto from "crypto"; + import * as net from "net"; + import * as stream from "stream"; + + var CLIENT_RENEG_LIMIT: number; + var CLIENT_RENEG_WINDOW: number; + + export interface TlsOptions { + host?: string; + port?: number; + pfx?: any; //string or buffer + key?: any; //string or buffer + passphrase?: string; + cert?: any; + ca?: any; //string or buffer + crl?: any; //string or string array + ciphers?: string; + honorCipherOrder?: any; + requestCert?: boolean; + rejectUnauthorized?: boolean; + NPNProtocols?: any; //array or Buffer; + SNICallback?: (servername: string) => any; + } + + export interface ConnectionOptions { + host?: string; + port?: number; + socket?: net.Socket; + pfx?: any; //string | Buffer + key?: any; //string | Buffer + passphrase?: string; + cert?: any; //string | Buffer + ca?: any; //Array of string | Buffer + rejectUnauthorized?: boolean; + NPNProtocols?: any; //Array of string | Buffer + servername?: string; + } + + export interface Server extends net.Server { + // Extended base methods + listen(port: number, host?: string, backlog?: number, listeningListener?: Function): Server; + listen(path: string, listeningListener?: Function): Server; + listen(handle: any, listeningListener?: Function): Server; + + listen(port: number, host?: string, callback?: Function): Server; + close(): Server; + address(): { port: number; family: string; address: string; }; + addContext(hostName: string, credentials: { + key: string; + cert: string; + ca: string; + }): void; + maxConnections: number; + connections: number; + } + + export interface ClearTextStream extends stream.Duplex { + authorized: boolean; + authorizationError: Error; + getPeerCertificate(): any; + getCipher: { + name: string; + version: string; + }; + address: { + port: number; + family: string; + address: string; + }; + remoteAddress: string; + remotePort: number; + } + + export interface SecurePair { + encrypted: any; + cleartext: any; + } + + export interface SecureContextOptions { + pfx?: any; //string | buffer + key?: any; //string | buffer + passphrase?: string; + cert?: any; // string | buffer + ca?: any; // string | buffer + crl?: any; // string | string[] + ciphers?: string; + honorCipherOrder?: boolean; + } + + export interface SecureContext { + context: any; + } + + export function createServer(options: TlsOptions, secureConnectionListener?: (cleartextStream: ClearTextStream) =>void ): Server; + export function connect(options: TlsOptions, secureConnectionListener?: () =>void ): ClearTextStream; + export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () =>void ): ClearTextStream; + export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () =>void ): ClearTextStream; + export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair; + export function createSecureContext(details: SecureContextOptions): SecureContext; +} + +declare module "crypto" { + export interface CredentialDetails { + pfx: string; + key: string; + passphrase: string; + cert: string; + ca: any; //string | string array + crl: any; //string | string array + ciphers: string; + } + export interface Credentials { context?: any; } + export function createCredentials(details: CredentialDetails): Credentials; + export function createHash(algorithm: string): Hash; + export function createHmac(algorithm: string, key: string): Hmac; + export function createHmac(algorithm: string, key: Buffer): Hmac; + export interface Hash { + update(data: any, input_encoding?: string): Hash; + digest(encoding: 'buffer'): Buffer; + digest(encoding: string): any; + digest(): Buffer; + } + export interface Hmac extends NodeJS.ReadWriteStream { + update(data: any, input_encoding?: string): Hmac; + digest(encoding: 'buffer'): Buffer; + digest(encoding: string): any; + digest(): Buffer; + } + export function createCipher(algorithm: string, password: any): Cipher; + export function createCipheriv(algorithm: string, key: any, iv: any): Cipher; + export interface Cipher { + update(data: Buffer): Buffer; + update(data: string, input_encoding?: string, output_encoding?: string): string; + final(): Buffer; + final(output_encoding: string): string; + setAutoPadding(auto_padding: boolean): void; + } + export function createDecipher(algorithm: string, password: any): Decipher; + export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher; + export interface Decipher { + update(data: Buffer): Buffer; + update(data: string, input_encoding?: string, output_encoding?: string): string; + final(): Buffer; + final(output_encoding: string): string; + setAutoPadding(auto_padding: boolean): void; + } + export function createSign(algorithm: string): Signer; + export interface Signer extends NodeJS.WritableStream { + update(data: any): void; + sign(private_key: string, output_format: string): string; + } + export function createVerify(algorith: string): Verify; + export interface Verify extends NodeJS.WritableStream { + update(data: any): void; + verify(object: string, signature: string, signature_format?: string): boolean; + } + export function createDiffieHellman(prime_length: number): DiffieHellman; + export function createDiffieHellman(prime: number, encoding?: string): DiffieHellman; + export interface DiffieHellman { + generateKeys(encoding?: string): string; + computeSecret(other_public_key: string, input_encoding?: string, output_encoding?: string): string; + getPrime(encoding?: string): string; + getGenerator(encoding: string): string; + getPublicKey(encoding?: string): string; + getPrivateKey(encoding?: string): string; + setPublicKey(public_key: string, encoding?: string): void; + setPrivateKey(public_key: string, encoding?: string): void; + } + export function getDiffieHellman(group_name: string): DiffieHellman; + export function pbkdf2(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, callback: (err: Error, derivedKey: Buffer) => any): void; + export function pbkdf2(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, digest: string, callback: (err: Error, derivedKey: Buffer) => any): void; + export function pbkdf2Sync(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number) : Buffer; + export function pbkdf2Sync(password: string|Buffer, salt: string|Buffer, iterations: number, keylen: number, digest: string) : Buffer; + export function randomBytes(size: number): Buffer; + export function randomBytes(size: number, callback: (err: Error, buf: Buffer) =>void ): void; + export function pseudoRandomBytes(size: number): Buffer; + export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) =>void ): void; +} + +declare module "stream" { + import * as events from "events"; + + export class Stream extends events.EventEmitter { + pipe(destination: T, options?: { end?: boolean; }): T; + } + + export interface ReadableOptions { + highWaterMark?: number; + encoding?: string; + objectMode?: boolean; + } + + export class Readable extends events.EventEmitter implements NodeJS.ReadableStream { + readable: boolean; + constructor(opts?: ReadableOptions); + _read(size: number): void; + read(size?: number): any; + setEncoding(encoding: string): void; + pause(): void; + resume(): void; + pipe(destination: T, options?: { end?: boolean; }): T; + unpipe(destination?: T): void; + unshift(chunk: any): void; + wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; + push(chunk: any, encoding?: string): boolean; + } + + export interface WritableOptions { + highWaterMark?: number; + decodeStrings?: boolean; + objectMode?: boolean; + } + + export class Writable extends events.EventEmitter implements NodeJS.WritableStream { + writable: boolean; + constructor(opts?: WritableOptions); + _write(chunk: any, encoding: string, callback: Function): void; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + end(): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + } + + export interface DuplexOptions extends ReadableOptions, WritableOptions { + allowHalfOpen?: boolean; + } + + // Note: Duplex extends both Readable and Writable. + export class Duplex extends Readable implements NodeJS.ReadWriteStream { + writable: boolean; + constructor(opts?: DuplexOptions); + _write(chunk: any, encoding: string, callback: Function): void; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + end(): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + } + + export interface TransformOptions extends ReadableOptions, WritableOptions {} + + // Note: Transform lacks the _read and _write methods of Readable/Writable. + export class Transform extends events.EventEmitter implements NodeJS.ReadWriteStream { + readable: boolean; + writable: boolean; + constructor(opts?: TransformOptions); + _transform(chunk: any, encoding: string, callback: Function): void; + _flush(callback: Function): void; + read(size?: number): any; + setEncoding(encoding: string): void; + pause(): void; + resume(): void; + pipe(destination: T, options?: { end?: boolean; }): T; + unpipe(destination?: T): void; + unshift(chunk: any): void; + wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; + push(chunk: any, encoding?: string): boolean; + write(chunk: any, cb?: Function): boolean; + write(chunk: any, encoding?: string, cb?: Function): boolean; + end(): void; + end(chunk: any, cb?: Function): void; + end(chunk: any, encoding?: string, cb?: Function): void; + } + + export class PassThrough extends Transform {} +} + +declare module "util" { + export interface InspectOptions { + showHidden?: boolean; + depth?: number; + colors?: boolean; + customInspect?: boolean; + } + + export function format(format: any, ...param: any[]): string; + export function debug(string: string): void; + export function error(...param: any[]): void; + export function puts(...param: any[]): void; + export function print(...param: any[]): void; + export function log(string: string): void; + export function inspect(object: any, showHidden?: boolean, depth?: number, color?: boolean): string; + export function inspect(object: any, options: InspectOptions): string; + export function isArray(object: any): boolean; + export function isRegExp(object: any): boolean; + export function isDate(object: any): boolean; + export function isError(object: any): boolean; + export function inherits(constructor: any, superConstructor: any): void; + export function debuglog(key:string): (msg:string,...param: any[])=>void; +} + +declare module "assert" { + function internal (value: any, message?: string): void; + module internal { + export class AssertionError implements Error { + name: string; + message: string; + actual: any; + expected: any; + operator: string; + generatedMessage: boolean; + + constructor(options?: {message?: string; actual?: any; expected?: any; + operator?: string; stackStartFunction?: Function}); + } + + export function fail(actual?: any, expected?: any, message?: string, operator?: string): void; + export function ok(value: any, message?: string): void; + export function equal(actual: any, expected: any, message?: string): void; + export function notEqual(actual: any, expected: any, message?: string): void; + export function deepEqual(actual: any, expected: any, message?: string): void; + export function notDeepEqual(acutal: any, expected: any, message?: string): void; + export function strictEqual(actual: any, expected: any, message?: string): void; + export function notStrictEqual(actual: any, expected: any, message?: string): void; + export function deepStrictEqual(actual: any, expected: any, message?: string): void; + export function notDeepStrictEqual(actual: any, expected: any, message?: string): void; + export var throws: { + (block: Function, message?: string): void; + (block: Function, error: Function, message?: string): void; + (block: Function, error: RegExp, message?: string): void; + (block: Function, error: (err: any) => boolean, message?: string): void; + }; + + export var doesNotThrow: { + (block: Function, message?: string): void; + (block: Function, error: Function, message?: string): void; + (block: Function, error: RegExp, message?: string): void; + (block: Function, error: (err: any) => boolean, message?: string): void; + }; + + export function ifError(value: any): void; + } + + export = internal; +} + +declare module "tty" { + import * as net from "net"; + + export function isatty(fd: number): boolean; + export interface ReadStream extends net.Socket { + isRaw: boolean; + setRawMode(mode: boolean): void; + isTTY: boolean; + } + export interface WriteStream extends net.Socket { + columns: number; + rows: number; + isTTY: boolean; + } +} + +declare module "domain" { + import * as events from "events"; + + export class Domain extends events.EventEmitter { + run(fn: Function): void; + add(emitter: events.EventEmitter): void; + remove(emitter: events.EventEmitter): void; + bind(cb: (err: Error, data: any) => any): any; + intercept(cb: (data: any) => any): any; + dispose(): void; + + addListener(event: string, listener: Function): Domain; + on(event: string, listener: Function): Domain; + once(event: string, listener: Function): Domain; + removeListener(event: string, listener: Function): Domain; + removeAllListeners(event?: string): Domain; + } + + export function create(): Domain; +} + +declare module "constants" { + export var E2BIG: number; + export var EACCES: number; + export var EADDRINUSE: number; + export var EADDRNOTAVAIL: number; + export var EAFNOSUPPORT: number; + export var EAGAIN: number; + export var EALREADY: number; + export var EBADF: number; + export var EBADMSG: number; + export var EBUSY: number; + export var ECANCELED: number; + export var ECHILD: number; + export var ECONNABORTED: number; + export var ECONNREFUSED: number; + export var ECONNRESET: number; + export var EDEADLK: number; + export var EDESTADDRREQ: number; + export var EDOM: number; + export var EEXIST: number; + export var EFAULT: number; + export var EFBIG: number; + export var EHOSTUNREACH: number; + export var EIDRM: number; + export var EILSEQ: number; + export var EINPROGRESS: number; + export var EINTR: number; + export var EINVAL: number; + export var EIO: number; + export var EISCONN: number; + export var EISDIR: number; + export var ELOOP: number; + export var EMFILE: number; + export var EMLINK: number; + export var EMSGSIZE: number; + export var ENAMETOOLONG: number; + export var ENETDOWN: number; + export var ENETRESET: number; + export var ENETUNREACH: number; + export var ENFILE: number; + export var ENOBUFS: number; + export var ENODATA: number; + export var ENODEV: number; + export var ENOENT: number; + export var ENOEXEC: number; + export var ENOLCK: number; + export var ENOLINK: number; + export var ENOMEM: number; + export var ENOMSG: number; + export var ENOPROTOOPT: number; + export var ENOSPC: number; + export var ENOSR: number; + export var ENOSTR: number; + export var ENOSYS: number; + export var ENOTCONN: number; + export var ENOTDIR: number; + export var ENOTEMPTY: number; + export var ENOTSOCK: number; + export var ENOTSUP: number; + export var ENOTTY: number; + export var ENXIO: number; + export var EOPNOTSUPP: number; + export var EOVERFLOW: number; + export var EPERM: number; + export var EPIPE: number; + export var EPROTO: number; + export var EPROTONOSUPPORT: number; + export var EPROTOTYPE: number; + export var ERANGE: number; + export var EROFS: number; + export var ESPIPE: number; + export var ESRCH: number; + export var ETIME: number; + export var ETIMEDOUT: number; + export var ETXTBSY: number; + export var EWOULDBLOCK: number; + export var EXDEV: number; + export var WSAEINTR: number; + export var WSAEBADF: number; + export var WSAEACCES: number; + export var WSAEFAULT: number; + export var WSAEINVAL: number; + export var WSAEMFILE: number; + export var WSAEWOULDBLOCK: number; + export var WSAEINPROGRESS: number; + export var WSAEALREADY: number; + export var WSAENOTSOCK: number; + export var WSAEDESTADDRREQ: number; + export var WSAEMSGSIZE: number; + export var WSAEPROTOTYPE: number; + export var WSAENOPROTOOPT: number; + export var WSAEPROTONOSUPPORT: number; + export var WSAESOCKTNOSUPPORT: number; + export var WSAEOPNOTSUPP: number; + export var WSAEPFNOSUPPORT: number; + export var WSAEAFNOSUPPORT: number; + export var WSAEADDRINUSE: number; + export var WSAEADDRNOTAVAIL: number; + export var WSAENETDOWN: number; + export var WSAENETUNREACH: number; + export var WSAENETRESET: number; + export var WSAECONNABORTED: number; + export var WSAECONNRESET: number; + export var WSAENOBUFS: number; + export var WSAEISCONN: number; + export var WSAENOTCONN: number; + export var WSAESHUTDOWN: number; + export var WSAETOOMANYREFS: number; + export var WSAETIMEDOUT: number; + export var WSAECONNREFUSED: number; + export var WSAELOOP: number; + export var WSAENAMETOOLONG: number; + export var WSAEHOSTDOWN: number; + export var WSAEHOSTUNREACH: number; + export var WSAENOTEMPTY: number; + export var WSAEPROCLIM: number; + export var WSAEUSERS: number; + export var WSAEDQUOT: number; + export var WSAESTALE: number; + export var WSAEREMOTE: number; + export var WSASYSNOTREADY: number; + export var WSAVERNOTSUPPORTED: number; + export var WSANOTINITIALISED: number; + export var WSAEDISCON: number; + export var WSAENOMORE: number; + export var WSAECANCELLED: number; + export var WSAEINVALIDPROCTABLE: number; + export var WSAEINVALIDPROVIDER: number; + export var WSAEPROVIDERFAILEDINIT: number; + export var WSASYSCALLFAILURE: number; + export var WSASERVICE_NOT_FOUND: number; + export var WSATYPE_NOT_FOUND: number; + export var WSA_E_NO_MORE: number; + export var WSA_E_CANCELLED: number; + export var WSAEREFUSED: number; + export var SIGHUP: number; + export var SIGINT: number; + export var SIGILL: number; + export var SIGABRT: number; + export var SIGFPE: number; + export var SIGKILL: number; + export var SIGSEGV: number; + export var SIGTERM: number; + export var SIGBREAK: number; + export var SIGWINCH: number; + export var SSL_OP_ALL: number; + export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number; + export var SSL_OP_CIPHER_SERVER_PREFERENCE: number; + export var SSL_OP_CISCO_ANYCONNECT: number; + export var SSL_OP_COOKIE_EXCHANGE: number; + export var SSL_OP_CRYPTOPRO_TLSEXT_BUG: number; + export var SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number; + export var SSL_OP_EPHEMERAL_RSA: number; + export var SSL_OP_LEGACY_SERVER_CONNECT: number; + export var SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number; + export var SSL_OP_MICROSOFT_SESS_ID_BUG: number; + export var SSL_OP_MSIE_SSLV2_RSA_PADDING: number; + export var SSL_OP_NETSCAPE_CA_DN_BUG: number; + export var SSL_OP_NETSCAPE_CHALLENGE_BUG: number; + export var SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number; + export var SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number; + export var SSL_OP_NO_COMPRESSION: number; + export var SSL_OP_NO_QUERY_MTU: number; + export var SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number; + export var SSL_OP_NO_SSLv2: number; + export var SSL_OP_NO_SSLv3: number; + export var SSL_OP_NO_TICKET: number; + export var SSL_OP_NO_TLSv1: number; + export var SSL_OP_NO_TLSv1_1: number; + export var SSL_OP_NO_TLSv1_2: number; + export var SSL_OP_PKCS1_CHECK_1: number; + export var SSL_OP_PKCS1_CHECK_2: number; + export var SSL_OP_SINGLE_DH_USE: number; + export var SSL_OP_SINGLE_ECDH_USE: number; + export var SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number; + export var SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number; + export var SSL_OP_TLS_BLOCK_PADDING_BUG: number; + export var SSL_OP_TLS_D5_BUG: number; + export var SSL_OP_TLS_ROLLBACK_BUG: number; + export var ENGINE_METHOD_DSA: number; + export var ENGINE_METHOD_DH: number; + export var ENGINE_METHOD_RAND: number; + export var ENGINE_METHOD_ECDH: number; + export var ENGINE_METHOD_ECDSA: number; + export var ENGINE_METHOD_CIPHERS: number; + export var ENGINE_METHOD_DIGESTS: number; + export var ENGINE_METHOD_STORE: number; + export var ENGINE_METHOD_PKEY_METHS: number; + export var ENGINE_METHOD_PKEY_ASN1_METHS: number; + export var ENGINE_METHOD_ALL: number; + export var ENGINE_METHOD_NONE: number; + export var DH_CHECK_P_NOT_SAFE_PRIME: number; + export var DH_CHECK_P_NOT_PRIME: number; + export var DH_UNABLE_TO_CHECK_GENERATOR: number; + export var DH_NOT_SUITABLE_GENERATOR: number; + export var NPN_ENABLED: number; + export var RSA_PKCS1_PADDING: number; + export var RSA_SSLV23_PADDING: number; + export var RSA_NO_PADDING: number; + export var RSA_PKCS1_OAEP_PADDING: number; + export var RSA_X931_PADDING: number; + export var RSA_PKCS1_PSS_PADDING: number; + export var POINT_CONVERSION_COMPRESSED: number; + export var POINT_CONVERSION_UNCOMPRESSED: number; + export var POINT_CONVERSION_HYBRID: number; + export var O_RDONLY: number; + export var O_WRONLY: number; + export var O_RDWR: number; + export var S_IFMT: number; + export var S_IFREG: number; + export var S_IFDIR: number; + export var S_IFCHR: number; + export var S_IFLNK: number; + export var O_CREAT: number; + export var O_EXCL: number; + export var O_TRUNC: number; + export var O_APPEND: number; + export var F_OK: number; + export var R_OK: number; + export var W_OK: number; + export var X_OK: number; + export var UV_UDP_REUSEADDR: number; +} diff --git a/typings/progress-stream.d.ts b/typings/progress-stream.d.ts new file mode 100644 index 00000000000..e2063c76564 --- /dev/null +++ b/typings/progress-stream.d.ts @@ -0,0 +1,16 @@ +declare module "progress-stream" { + import ReadWriteStream = NodeJS.ReadWriteStream; + interface Options { + time?: number + length?: number + } + + interface Progress { + percentage: number + delta: number + } + + function progress(options: Options, onProgress?: (progress: Progress) => void): ReadWriteStream + + export = progress +} \ No newline at end of file diff --git a/typings/rimraf.d.ts b/typings/rimraf.d.ts new file mode 100644 index 00000000000..d96c30fa093 --- /dev/null +++ b/typings/rimraf.d.ts @@ -0,0 +1,16 @@ +// Type definitions for rimraf +// Project: https://github.com/isaacs/rimraf +// Definitions by: Carlos Ballesteros Velasco +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// Imported from: https://github.com/soywiz/typescript-node-definitions/rimraf.d.ts + +declare module "rimraf" { + function rimraf(path: string, opts: any | ((error: Error) => void), callback?: (error: Error) => void): void; + module rimraf { + export function sync(path: string): void; + export var EMFILE_MAX: number; + export var BUSYTRIES_MAX: number; + } + export = rimraf; +}