From 301b0c40ac08b3cbd3b11106e1269731f0dc2d74 Mon Sep 17 00:00:00 2001 From: Jed Mao Date: Mon, 1 Dec 2014 03:13:46 -0600 Subject: [PATCH] Introduce iso http modules --- .travis.yml | 13 +- bower.json | 3 +- bower_components/dt-node/.bower.json | 18 + bower_components/dt-node/README.md | 10 + bower_components/dt-node/bower.json | 7 + bower_components/dt-node/node.d.ts | 1336 +++++++++++++++++ ...pec.d.ts => BrowserHttp.browser.spec.d.ts} | 0 d.ts/BrowserHttp.d.ts | 12 + d.ts/FakeHttp.d.ts | 12 + d.ts/{http.spec.d.ts => FakeHttp.spec.d.ts} | 0 d.ts/Helpers.d.ts | 2 + d.ts/Helpers.spec.d.ts | 1 + d.ts/IsoHttp.d.ts | 42 + d.ts/IsoHttp.spec.d.ts | 2 + d.ts/NodeHttp.d.ts | 9 + d.ts/NodeHttp.spec.d.ts | 2 + d.ts/TestHelpers.d.ts | 3 + d.ts/TestHelpers.spec.d.ts | 1 + d.ts/Types.d.ts | 3 + d.ts/http.browser.d.ts | 2 - d.ts/http.d.ts | 2 - d.ts/runIsomorphicTests.d.ts | 4 + d.ts/server.d.ts | 3 + dist/iso-http.js | 159 +- dist/iso-http.min.js | 2 +- gulpfile.js | 17 +- iso-http.d.ts | 4 +- iso-http.sln.DotSettings | 6 + js/BrowserHttp.browser.spec.js | 17 + js/BrowserHttp.js | 81 + js/FakeHttp.js | 39 + js/FakeHttp.spec.js | 41 + js/Helpers.js | 14 + js/Helpers.spec.js | 40 + js/IsoHttp.js | 52 + js/IsoHttp.spec.js | 47 + js/NodeHttp.js | 61 + js/NodeHttp.spec.js | 18 + js/TestHelpers.js | 16 + js/TestHelpers.spec.js | 29 + js/Types.js | 1 + js/http.browser.js | 6 - js/http.browser.spec.js | 6 - js/http.js | 6 - js/http.spec.js | 6 - js/runIsomorphicTests.js | 64 + js/server.js | 16 + karma.conf.js | 26 +- lib/BrowserHttp.browser.spec.ts | 23 + lib/BrowserHttp.ts | 84 ++ lib/FakeHttp.spec.ts | 50 + lib/FakeHttp.ts | 39 + lib/Helpers.spec.ts | 53 + lib/Helpers.ts | 13 + lib/IsoHttp.spec.ts | 56 + lib/IsoHttp.ts | 93 ++ lib/NodeHttp.spec.ts | 24 + lib/NodeHttp.ts | 57 + lib/TestHelpers.spec.ts | 42 + lib/TestHelpers.ts | 15 + lib/Types.ts | 3 + lib/http.browser.spec.ts | 9 - lib/http.browser.ts | 3 - lib/http.spec.ts | 9 - lib/http.ts | 3 - lib/runIsomorphicTests.ts | 74 + lib/server.ts | 23 + package.json | 10 +- tasks/browserify.js | 2 +- tasks/test--watch.js | 12 - tasks/test.js | 33 +- tasks/watch.js | 5 - { | 0 73 files changed, 2878 insertions(+), 118 deletions(-) create mode 100644 bower_components/dt-node/.bower.json create mode 100644 bower_components/dt-node/README.md create mode 100644 bower_components/dt-node/bower.json create mode 100644 bower_components/dt-node/node.d.ts rename d.ts/{http.browser.spec.d.ts => BrowserHttp.browser.spec.d.ts} (100%) create mode 100644 d.ts/BrowserHttp.d.ts create mode 100644 d.ts/FakeHttp.d.ts rename d.ts/{http.spec.d.ts => FakeHttp.spec.d.ts} (100%) create mode 100644 d.ts/Helpers.d.ts create mode 100644 d.ts/Helpers.spec.d.ts create mode 100644 d.ts/IsoHttp.d.ts create mode 100644 d.ts/IsoHttp.spec.d.ts create mode 100644 d.ts/NodeHttp.d.ts create mode 100644 d.ts/NodeHttp.spec.d.ts create mode 100644 d.ts/TestHelpers.d.ts create mode 100644 d.ts/TestHelpers.spec.d.ts create mode 100644 d.ts/Types.d.ts delete mode 100644 d.ts/http.browser.d.ts delete mode 100644 d.ts/http.d.ts create mode 100644 d.ts/runIsomorphicTests.d.ts create mode 100644 d.ts/server.d.ts create mode 100644 iso-http.sln.DotSettings create mode 100644 js/BrowserHttp.browser.spec.js create mode 100644 js/BrowserHttp.js create mode 100644 js/FakeHttp.js create mode 100644 js/FakeHttp.spec.js create mode 100644 js/Helpers.js create mode 100644 js/Helpers.spec.js create mode 100644 js/IsoHttp.js create mode 100644 js/IsoHttp.spec.js create mode 100644 js/NodeHttp.js create mode 100644 js/NodeHttp.spec.js create mode 100644 js/TestHelpers.js create mode 100644 js/TestHelpers.spec.js create mode 100644 js/Types.js delete mode 100644 js/http.browser.js delete mode 100644 js/http.browser.spec.js delete mode 100644 js/http.js delete mode 100644 js/http.spec.js create mode 100644 js/runIsomorphicTests.js create mode 100644 js/server.js create mode 100644 lib/BrowserHttp.browser.spec.ts create mode 100644 lib/BrowserHttp.ts create mode 100644 lib/FakeHttp.spec.ts create mode 100644 lib/FakeHttp.ts create mode 100644 lib/Helpers.spec.ts create mode 100644 lib/Helpers.ts create mode 100644 lib/IsoHttp.spec.ts create mode 100644 lib/IsoHttp.ts create mode 100644 lib/NodeHttp.spec.ts create mode 100644 lib/NodeHttp.ts create mode 100644 lib/TestHelpers.spec.ts create mode 100644 lib/TestHelpers.ts create mode 100644 lib/Types.ts delete mode 100644 lib/http.browser.spec.ts delete mode 100644 lib/http.browser.ts delete mode 100644 lib/http.spec.ts delete mode 100644 lib/http.ts create mode 100644 lib/runIsomorphicTests.ts create mode 100644 lib/server.ts delete mode 100644 tasks/test--watch.js delete mode 100644 tasks/watch.js create mode 100644 { diff --git a/.travis.yml b/.travis.yml index 94cc77d..2894f16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,10 @@ node_js: sudo: false -notifications: - email: - on_success: change - on_failure: always +before_install: + - export CHROME_BIN=chromium-browser + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start after_success: - npm install -g codeclimate-test-reporter @@ -17,3 +17,8 @@ after_success: addons: code_climate: repo_token: f159eec3f70f0484d592e32cd87160b251249591a1f8007bea22ac4b819ffe45 + +notifications: + email: + on_success: change + on_failure: always diff --git a/bower.json b/bower.json index adf251b..9ba5dce 100644 --- a/bower.json +++ b/bower.json @@ -26,6 +26,7 @@ "tests" ], "devDependencies": { - "dt-jasmine": "~2.0.0" + "dt-jasmine": "~2.0.0", + "dt-node": "https://github.com/jedmao/dt-node.git#~0.11.13" } } diff --git a/bower_components/dt-node/.bower.json b/bower_components/dt-node/.bower.json new file mode 100644 index 0000000..4bd9bba --- /dev/null +++ b/bower_components/dt-node/.bower.json @@ -0,0 +1,18 @@ +{ + "name": "dt-node-jedmao", + "version": "0.11.13", + "ignore": [ + "**/.*" + ], + "homepage": "https://github.com/jedmao/dt-node", + "_release": "0.11.13", + "_resolution": { + "type": "version", + "tag": "v0.11.13", + "commit": "390428ec4782a33b4a1262c3c92450d6eb6e929e" + }, + "_source": "https://github.com/jedmao/dt-node.git", + "_target": "~0.11.13", + "_originalSource": "https://github.com/jedmao/dt-node.git", + "_direct": true +} \ No newline at end of file diff --git a/bower_components/dt-node/README.md b/bower_components/dt-node/README.md new file mode 100644 index 0000000..8dd2c90 --- /dev/null +++ b/bower_components/dt-node/README.md @@ -0,0 +1,10 @@ +# dt-node + +TypeScript Definitions for Node from DefinitelyTyped + + +## Installation + +``` +$ bower install --save dt-node-jedmao#0.11 +``` diff --git a/bower_components/dt-node/bower.json b/bower_components/dt-node/bower.json new file mode 100644 index 0000000..21a0d1b --- /dev/null +++ b/bower_components/dt-node/bower.json @@ -0,0 +1,7 @@ +{ + "name": "dt-node-jedmao", + "version": "0.11.13", + "ignore": [ + "**/.*" + ] +} diff --git a/bower_components/dt-node/node.d.ts b/bower_components/dt-node/node.d.ts new file mode 100644 index 0000000..1067aa0 --- /dev/null +++ b/bower_components/dt-node/node.d.ts @@ -0,0 +1,1336 @@ +// Type definitions for Node.js v0.11.13 +// Project: http://nodejs.org/ +// Definitions by: Microsoft TypeScript , DefinitelyTyped +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +/************************************************ +* * +* Node.js v0.11.13 API * +* * +************************************************/ + +/************************************************ +* * +* GLOBAL * +* * +************************************************/ +declare var process: NodeJS.Process; +declare var global: any; + +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; + +declare var require: { + (id: string): any; + resolve(id: string): string; + cache: any; + extensions: any; + main: any; +}; + +declare var module: { + exports: any; + require(id: string): any; + id: string; + filename: string; + loaded: boolean; + parent: any; + children: any[]; +}; + +// 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 { } +declare var Buffer: { + 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; +}; + +/************************************************ +* * +* GLOBAL INTERFACES * +* * +************************************************/ +declare module NodeJS { + export interface ErrnoException extends Error { + errno?: any; + code?: string; + path?: string; + syscall?: 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): void; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + } + + export interface ReadableStream extends EventEmitter { + readable: boolean; + 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: string): void; + unshift(chunk: Buffer): void; + wrap(oldStream: ReadableStream): ReadableStream; + } + + export interface WritableStream extends EventEmitter { + writable: boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: 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): 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 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; + copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number; + slice(start?: number, end?: number): Buffer; + 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): void; + writeUInt16LE(value: number, offset: number, noAssert?: boolean): void; + writeUInt16BE(value: number, offset: number, noAssert?: boolean): void; + writeUInt32LE(value: number, offset: number, noAssert?: boolean): void; + writeUInt32BE(value: number, offset: number, noAssert?: boolean): void; + writeInt8(value: number, offset: number, noAssert?: boolean): void; + writeInt16LE(value: number, offset: number, noAssert?: boolean): void; + writeInt16BE(value: number, offset: number, noAssert?: boolean): void; + writeInt32LE(value: number, offset: number, noAssert?: boolean): void; + writeInt32BE(value: number, offset: number, noAssert?: boolean): void; + writeFloatLE(value: number, offset: number, noAssert?: boolean): void; + writeFloatBE(value: number, offset: number, noAssert?: boolean): void; + writeDoubleLE(value: number, offset: number, noAssert?: boolean): void; + writeDoubleBE(value: number, offset: number, noAssert?: boolean): void; + fill(value: any, offset?: number, end?: number): void; +} + +/************************************************ +* * +* MODULES * +* * +************************************************/ +declare module "buffer" { + export var INSPECT_MAX_BYTES: number; +} + +declare module "querystring" { + export function stringify(obj: any, sep?: string, eq?: string): string; + export function parse(str: string, sep?: string, eq?: string, options?: { maxKeys?: number; }): any; + export function escape(): any; + export function unescape(): any; +} + +declare module "events" { + export class EventEmitter implements NodeJS.EventEmitter { + static listenerCount(emitter: EventEmitter, event: string): 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): void; + listeners(event: string): Function[]; + emit(event: string, ...args: any[]): boolean; + } +} + +declare module "http" { + import events = require("events"); + import net = require("net"); + import stream = require("stream"); + + export interface Server extends events.EventEmitter { + listen(port: number, hostname?: string, backlog?: number, 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; + } + export interface ServerRequest extends events.EventEmitter, stream.Readable { + method: string; + url: string; + headers: any; + trailers: string; + httpVersion: string; + setEncoding(encoding?: string): void; + pause(): void; + resume(): void; + 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; + 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 ClientResponse extends events.EventEmitter, stream.Readable { + statusCode: number; + httpVersion: string; + headers: any; + trailers: any; + setEncoding(encoding?: string): void; + pause(): void; + resume(): void; + } + export interface Agent { maxSockets: number; sockets: any; requests: any; } + + export var STATUS_CODES: { + [errorCode: number]: string; + [errorCode: string]: string; + }; + export function createServer(requestListener?: (request: ServerRequest, response: ServerResponse) => void): Server; + export function createClient(port?: number, host?: string): any; + export function request(options: any, callback?: Function): ClientRequest; + export function get(options: any, callback?: Function): ClientRequest; + export var globalAgent: Agent; +} + +declare module "cluster" { + import child = require("child_process"); + import events = require("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: 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 stream = require("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 deflateRaw(buf: Buffer, callback: (error: Error, result: any) => void): void; + export function gzip(buf: Buffer, callback: (error: Error, result: any) => void): void; + export function gunzip(buf: Buffer, callback: (error: Error, result: any) => void): void; + export function inflate(buf: Buffer, callback: (error: Error, result: any) => void): void; + export function inflateRaw(buf: Buffer, callback: (error: Error, result: any) => void): void; + export function unzip(buf: Buffer, callback: (error: Error, result: any) => void): void; + + // 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 function tmpDir(): 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(): { model: string; speed: number; times: { user: number; nice: number; sys: number; idle: number; irq: number; }; }[]; + export function networkInterfaces(): any; + export var EOL: string; +} + +declare module "https" { + import tls = require("tls"); + import events = require("events"); + import http = require("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 { + host?: string; + hostname?: string; + port?: number; + path?: string; + method?: string; + headers?: any; + auth?: string; + agent?: any; + pfx?: any; + key?: any; + passphrase?: string; + cert?: any; + ca?: any; + ciphers?: string; + rejectUnauthorized?: boolean; + } + + 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: events.EventEmitter) => void): http.ClientRequest; + export function get(options: RequestOptions, callback?: (res: events.EventEmitter) => 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): string; + encode(codePoints: number[]): string; + } + export var version: any; +} + +declare module "repl" { + import stream = require("stream"); + import events = require("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 events = require("events"); + import stream = require("stream"); + + export interface ReadLine extends events.EventEmitter { + setPrompt(prompt: string, length: number): void; + prompt(preserveCursor?: boolean): void; + question(query: string, callback: Function): void; + pause(): void; + resume(): void; + close(): void; + write(data: any, key?: any): void; + } + export interface ReadLineOptions { + input: NodeJS.ReadableStream; + output: NodeJS.WritableStream; + completer?: Function; + terminal?: boolean; + } + export function createInterface(options: ReadLineOptions): ReadLine; +} + +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 events = require("events"); + import stream = require("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; + } + + 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, args: string[], options: { + cwd?: string; + stdio?: any; + customFds?: any; + env?: any; + encoding?: string; + timeout?: number; + maxBuffer?: string; + killSignal?: string; + }, callback: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess; + export function fork(modulePath: string, args?: string[], options?: { + cwd?: string; + env?: any; + encoding?: string; + }): ChildProcess; +} + +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 interface UrlOptions { + protocol?: string; + auth?: string; + hostname?: string; + port?: string; + host?: string; + pathname?: string; + search?: string; + query?: any; + hash?: string; + path?: string; + } + + export function parse(urlStr: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url; + export function format(url: UrlOptions): 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 stream = require("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; }; + remoteAddress: string; + remotePort: 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: { 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: { 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 events = require("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 stream = require("stream"); + import events = require("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; + } + + interface FSWatcher extends events.EventEmitter { + close(): void; + } + + export interface ReadStream extends stream.Readable { } + export interface WriteStream extends stream.Writable { } + + export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + 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; + export function unlink(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function unlinkSync(path: string): void; + export function rmdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function rmdirSync(path: string): void; + export function mkdir(path: string, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function mkdir(path: string, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void; + export function mkdir(path: string, mode: string, callback?: (err?: NodeJS.ErrnoException) => 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: 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 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; + export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void; + export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void; + export function readFileSync(filename: string, encoding: string): string; + export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string; + export function readFileSync(filename: string, options?: { flag?: string; }): Buffer; + export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; + export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void; + export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: 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; + export function createReadStream(path: string, options?: { + flags?: string; + encoding?: string; + fd?: string; + mode?: number; + bufferSize?: number; + }): ReadStream; + export function createReadStream(path: string, options?: { + flags?: string; + encoding?: string; + fd?: string; + mode?: string; + bufferSize?: number; + }): ReadStream; + export function createWriteStream(path: string, options?: { + flags?: string; + encoding?: string; + string?: string; + }): WriteStream; +} + +declare module "path" { + export function normalize(p: string): string; + export function join(...paths: any[]): string; + export function resolve(...pathSegments: any[]): string; + 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; +} + +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 crypto = require("crypto"); + import net = require("net"); + import stream = require("stream"); + + var CLIENT_RENEG_LIMIT: number; + var CLIENT_RENEG_WINDOW: number; + + export interface TlsOptions { + 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 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; +} + +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; + interface Hash { + update(data: any, input_encoding?: string): Hash; + digest(encoding: 'buffer'): Buffer; + digest(encoding: string): any; + digest(): Buffer; + } + interface Hmac { + 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; + interface Cipher { + update(data: any, input_encoding?: string, output_encoding?: string): string; + 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; + interface Decipher { + update(data: any, input_encoding?: string, output_encoding?: string): void; + final(output_encoding?: string): string; + setAutoPadding(auto_padding: boolean): void; + } + export function createSign(algorithm: string): Signer; + interface Signer { + update(data: any): void; + sign(private_key: string, output_format: string): string; + } + export function createVerify(algorith: string): Verify; + interface Verify { + 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; + 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, salt: string, iterations: number, keylen: number, callback: (err: Error, derivedKey: Buffer) => any): void; + export function pbkdf2Sync(password: string, salt: string, iterations: number, keylen: number): 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 events = require("events"); + + export interface 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: string): void; + unshift(chunk: Buffer): void; + wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; + push(chunk: any, encoding?: string): boolean; + } + + export interface WritableOptions { + highWaterMark?: number; + decodeStrings?: boolean; + } + + export class Writable extends events.EventEmitter implements NodeJS.WritableStream { + writable: boolean; + constructor(opts?: WritableOptions); + _write(data: Buffer, encoding: string, callback: Function): void; + _write(data: string, encoding: string, callback: Function): void; + write(buffer: Buffer, cb?: Function): boolean; + write(str: 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 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(data: Buffer, encoding: string, callback: Function): void; + _write(data: string, encoding: string, callback: Function): void; + write(buffer: Buffer, cb?: Function): boolean; + write(str: 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 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: Buffer, encoding: string, callback: Function): void; + _transform(chunk: string, 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: string): void; + unshift(chunk: Buffer): void; + wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream; + push(chunk: any, encoding?: string): boolean; + write(buffer: Buffer, cb?: Function): boolean; + write(str: 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 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 deprecate(fn: Function, msg: string): Function; + export function debuglog(set: string): Function; + 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 isBoolean(arg: any): boolean; + export function isNull(arg: any): boolean; + export function isNullOrUndefined(arg: any): boolean; + export function isNumber(arg: any): boolean; + export function isString(arg: any): boolean; + export function isSymbol(arg: any): boolean; + export function isUndefined(arg: any): boolean; + export function isRegExp(arg: any): boolean; + export function isObject(arg: any): boolean; + export function isDate(arg: any): boolean; + export function isError(arg: any): boolean; + export function isFunction(arg: any): boolean; + export function isPrimitive(arg: any): boolean; + export function isBuffer(arg: any): boolean; + export function log(...arg: any[]): void + export function inherits(constructor: any, superConstructor: any): void; + export function p(...arg: any[]): void; + export function exec(...arg: any[]): void; + export function print(...arg: any[]): void; + export function puts(...arg: any[]): void; + export function debug(string: string): void; + export function error(...arg: 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 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 net = require("net"); + + export function isatty(fd: number): boolean; + export interface ReadStream extends net.Socket { + isRaw: boolean; + setRawMode(mode: boolean): void; + } + export interface WriteStream extends net.Socket { + columns: number; + rows: number; + } +} + +declare module "domain" { + import events = require("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; +} diff --git a/d.ts/http.browser.spec.d.ts b/d.ts/BrowserHttp.browser.spec.d.ts similarity index 100% rename from d.ts/http.browser.spec.d.ts rename to d.ts/BrowserHttp.browser.spec.d.ts diff --git a/d.ts/BrowserHttp.d.ts b/d.ts/BrowserHttp.d.ts new file mode 100644 index 0000000..6e6c970 --- /dev/null +++ b/d.ts/BrowserHttp.d.ts @@ -0,0 +1,12 @@ +import IsoHttp = require('./IsoHttp'); +declare module BrowserHttp { + function request(options: IsoHttp.RequestOptions, resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback): void; + class Agent extends IsoHttp.Agent { + constructor(options?: IsoHttp.RequestOptions); + send(resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback): void; + private setXhrHeaders(xhr, headers); + private createXhr(); + private parseHeaders(headers); + } +} +export = BrowserHttp; diff --git a/d.ts/FakeHttp.d.ts b/d.ts/FakeHttp.d.ts new file mode 100644 index 0000000..9191d3e --- /dev/null +++ b/d.ts/FakeHttp.d.ts @@ -0,0 +1,12 @@ +import IsoHttp = require('./IsoHttp'); +declare module FakeHttp { + function request(options?: IsoHttp.RequestOptions, resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback): Agent; + class Agent extends IsoHttp.Agent { + private resolve; + private reject; + send(resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback): Agent; + respondWith(response: IsoHttp.Response): void; + rejectWith(err: Error): void; + } +} +export = FakeHttp; diff --git a/d.ts/http.spec.d.ts b/d.ts/FakeHttp.spec.d.ts similarity index 100% rename from d.ts/http.spec.d.ts rename to d.ts/FakeHttp.spec.d.ts diff --git a/d.ts/Helpers.d.ts b/d.ts/Helpers.d.ts new file mode 100644 index 0000000..dcd4634 --- /dev/null +++ b/d.ts/Helpers.d.ts @@ -0,0 +1,2 @@ +export declare function joinUrlWithQuery(url: string, query?: {}): string; +export declare function serializeObject(obj: {}): string; diff --git a/d.ts/Helpers.spec.d.ts b/d.ts/Helpers.spec.d.ts new file mode 100644 index 0000000..e621b51 --- /dev/null +++ b/d.ts/Helpers.spec.d.ts @@ -0,0 +1 @@ +/// diff --git a/d.ts/IsoHttp.d.ts b/d.ts/IsoHttp.d.ts new file mode 100644 index 0000000..9ee0fdb --- /dev/null +++ b/d.ts/IsoHttp.d.ts @@ -0,0 +1,42 @@ +import Types = require('./Types'); +declare module IsoHttp { + interface Request { + (options: RequestOptions, resolve?: ResolveCallback, reject?: RejectCallback): void; + } + interface ResolveCallback { + (response: Response): void; + } + interface RejectCallback { + (error: ClientError): void; + } + class Agent { + protected url: string; + protected method: string; + protected headers: Types.HashTable; + protected data: Types.HashTable; + protected withCredentials: boolean; + contentType: string; + constructor(options: RequestOptions); + setHeaders(headers: Types.HashTable): void; + send(resolve?: ResolveCallback, reject?: RejectCallback): void; + protected addRequestInfo(err: Error): ClientError; + } + interface Response { + headers: Types.HashTable; + status: number; + text: string; + } + interface RequestOptions { + contentType?: string; + data?: any; + headers?: any; + method?: string; + url?: string; + withCredentials?: boolean; + } + interface ClientError extends Error { + method: string; + url: string; + } +} +export = IsoHttp; diff --git a/d.ts/IsoHttp.spec.d.ts b/d.ts/IsoHttp.spec.d.ts new file mode 100644 index 0000000..f40d2e7 --- /dev/null +++ b/d.ts/IsoHttp.spec.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/d.ts/NodeHttp.d.ts b/d.ts/NodeHttp.d.ts new file mode 100644 index 0000000..eae43c9 --- /dev/null +++ b/d.ts/NodeHttp.d.ts @@ -0,0 +1,9 @@ +/// +import IsoHttp = require('./IsoHttp'); +declare module NodeHttp { + function request(options: IsoHttp.RequestOptions, resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback): void; + class Agent extends IsoHttp.Agent { + send(resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback): void; + } +} +export = NodeHttp; diff --git a/d.ts/NodeHttp.spec.d.ts b/d.ts/NodeHttp.spec.d.ts new file mode 100644 index 0000000..f40d2e7 --- /dev/null +++ b/d.ts/NodeHttp.spec.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/d.ts/TestHelpers.d.ts b/d.ts/TestHelpers.d.ts new file mode 100644 index 0000000..46a4b2a --- /dev/null +++ b/d.ts/TestHelpers.d.ts @@ -0,0 +1,3 @@ +export declare function getApiPath(path: string): string; +export declare function isPlainObject(obj: Object): boolean; +export declare function noop(): void; diff --git a/d.ts/TestHelpers.spec.d.ts b/d.ts/TestHelpers.spec.d.ts new file mode 100644 index 0000000..e621b51 --- /dev/null +++ b/d.ts/TestHelpers.spec.d.ts @@ -0,0 +1 @@ +/// diff --git a/d.ts/Types.d.ts b/d.ts/Types.d.ts new file mode 100644 index 0000000..8fa2978 --- /dev/null +++ b/d.ts/Types.d.ts @@ -0,0 +1,3 @@ +export interface HashTable { + [key: string]: T; +} diff --git a/d.ts/http.browser.d.ts b/d.ts/http.browser.d.ts deleted file mode 100644 index e1ebdea..0000000 --- a/d.ts/http.browser.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare class Http { -} diff --git a/d.ts/http.d.ts b/d.ts/http.d.ts deleted file mode 100644 index e1ebdea..0000000 --- a/d.ts/http.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export declare class Http { -} diff --git a/d.ts/runIsomorphicTests.d.ts b/d.ts/runIsomorphicTests.d.ts new file mode 100644 index 0000000..95935d7 --- /dev/null +++ b/d.ts/runIsomorphicTests.d.ts @@ -0,0 +1,4 @@ +/// +import IsoHttp = require('./IsoHttp'); +declare function runIsomorphicTests(request: IsoHttp.Request): void; +export = runIsomorphicTests; diff --git a/d.ts/server.d.ts b/d.ts/server.d.ts new file mode 100644 index 0000000..3bcd271 --- /dev/null +++ b/d.ts/server.d.ts @@ -0,0 +1,3 @@ +/// +declare var server: any; +export = server; diff --git a/dist/iso-http.js b/dist/iso-http.js index 08d938d..d69d08a 100644 --- a/dist/iso-http.js +++ b/dist/iso-http.js @@ -1,9 +1,154 @@ -require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o -1) ? '&' : '?'; + return url + joiner + serializeObject(query); +} +exports.joinUrlWithQuery = joinUrlWithQuery; +function serializeObject(obj) { + return Object.keys(obj).map(function (key) { + return key + '=' + encodeURIComponent(obj[key]); + }).join('&'); +} +exports.serializeObject = serializeObject; -},{}]},{},[]); +},{}],2:[function(require,module,exports){ +var IsoHttp; +(function (IsoHttp) { + var Agent = (function () { + function Agent(options) { + this.headers = {}; + if (!options || !Object.keys(options).length) { + throw new Error('Missing options.'); + } + if (!options.url) { + throw new Error('Missing required option: url.'); + } + this.url = options.url; + this.method = (options.method || 'GET').toUpperCase(); + if (options.headers) { + this.setHeaders(options.headers); + } + if (options.contentType) { + this.contentType = options.contentType; + } + this.withCredentials = options.withCredentials || false; + this.data = options.data || {}; + } + Object.defineProperty(Agent.prototype, "contentType", { + get: function () { + return this.headers['content-type']; + }, + set: function (value) { + this.headers['content-type'] = value; + }, + enumerable: true, + configurable: true + }); + Agent.prototype.setHeaders = function (headers) { + var _this = this; + Object.keys(headers).forEach(function (fieldName) { + _this.headers[fieldName] = headers[fieldName]; + }); + }; + Agent.prototype.send = function (resolve, reject) { + throw new Error('Not implemented.'); + }; + Agent.prototype.addRequestInfo = function (err) { + var result = err; + result.method = this.method; + result.url = this.url; + return result; + }; + return Agent; + })(); + IsoHttp.Agent = Agent; +})(IsoHttp || (IsoHttp = {})); +module.exports = IsoHttp; + +},{}],"iso-http":[function(require,module,exports){ +/* istanbul ignore next: TypeScript extend */ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Helpers = require('./Helpers'); +var IsoHttp = require('./IsoHttp'); +var BrowserHttp; +(function (BrowserHttp) { + function request(options, resolve, reject) { + new Agent(options).send(resolve, reject); + } + BrowserHttp.request = request; + var Agent = (function (_super) { + __extends(Agent, _super); + function Agent(options) { + _super.call(this, options); + var xrw = 'x-requested-with'; + this.headers[xrw] = this.headers[xrw] || 'XMLHttpRequest'; + } + Agent.prototype.send = function (resolve, reject) { + var _this = this; + var url = (this.method === 'GET') ? Helpers.joinUrlWithQuery(this.url, this.data) : this.url; + var xhr = this.createXhr(); + xhr.open(this.method, url, true); + if (this.withCredentials) { + xhr.withCredentials = true; + } + this.setXhrHeaders(xhr, this.headers); + xhr.onreadystatechange = function () { + if (xhr.readyState !== 4) { + return; + } + if (xhr.status === 0) { + reject(_this.addRequestInfo(new Error('Unspecified client error.'))); + return; + } + if (typeof resolve !== 'function') { + return; + } + resolve({ + status: xhr.status, + headers: _this.parseHeaders(xhr.getAllResponseHeaders()), + text: xhr.responseText + }); + }; + xhr.send(this.method !== 'GET' && JSON.stringify(this.data)); + }; + Agent.prototype.setXhrHeaders = function (xhr, headers) { + Object.keys(headers).forEach(function (name) { + xhr.setRequestHeader(name, headers[name]); + }); + }; + Agent.prototype.createXhr = function () { + try { + return new XMLHttpRequest(); + } + catch (err) { + throw new Error('Unsupported browser: Failed to create XHR object.'); + } + }; + Agent.prototype.parseHeaders = function (headers) { + var lines = headers.split(/\r?\n/); + lines.pop(); // final newline + var fields = {}; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var pos = line.indexOf(':'); + var field = line.slice(0, pos).toLowerCase(); + var value = line.slice(pos + 1).trim(); + fields[field] = value; + } + return fields; + }; + return Agent; + })(IsoHttp.Agent); + BrowserHttp.Agent = Agent; +})(BrowserHttp || (BrowserHttp = {})); +module.exports = BrowserHttp; + +},{"./Helpers":1,"./IsoHttp":2}]},{},[]); diff --git a/dist/iso-http.min.js b/dist/iso-http.min.js index 126c055..20f6053 100644 --- a/dist/iso-http.min.js +++ b/dist/iso-http.min.js @@ -1 +1 @@ -require=function r(n,t,e){function o(i,f){if(!t[i]){if(!n[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=t[i]={exports:{}};n[i][0].call(p.exports,function(r){var t=n[i][1][r];return o(t?t:r)},p,p.exports,r,n,t,e)}return t[i].exports}for(var u="function"==typeof require&&require,i=0;i-1?"&":"?";return e+r+o(t)}function o(e){return Object.keys(e).map(function(t){return t+"="+encodeURIComponent(e[t])}).join("&")}r.joinUrlWithQuery=n,r.serializeObject=o},{}],2:[function(e,t){var r;!function(e){var t=function(){function e(e){if(this.headers={},!e||!Object.keys(e).length)throw new Error("Missing options.");if(!e.url)throw new Error("Missing required option: url.");this.url=e.url,this.method=(e.method||"GET").toUpperCase(),e.headers&&this.setHeaders(e.headers),e.contentType&&(this.contentType=e.contentType),this.withCredentials=e.withCredentials||!1,this.data=e.data||{}}return Object.defineProperty(e.prototype,"contentType",{get:function(){return this.headers["content-type"]},set:function(e){this.headers["content-type"]=e},enumerable:!0,configurable:!0}),e.prototype.setHeaders=function(e){var t=this;Object.keys(e).forEach(function(r){t.headers[r]=e[r]})},e.prototype.send=function(){throw new Error("Not implemented.")},e.prototype.addRequestInfo=function(e){var t=e;return t.method=this.method,t.url=this.url,t},e}();e.Agent=t}(r||(r={})),t.exports=r},{}],"iso-http":[function(e,t){var r,n=this.__extends||function(e,t){function r(){this.constructor=e}for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);r.prototype=t.prototype,e.prototype=new r},o=e("./Helpers"),i=e("./IsoHttp");!function(e){function t(e,t,n){new r(e).send(t,n)}e.request=t;var r=function(e){function t(t){e.call(this,t);var r="x-requested-with";this.headers[r]=this.headers[r]||"XMLHttpRequest"}return n(t,e),t.prototype.send=function(e,t){var r=this,n="GET"===this.method?o.joinUrlWithQuery(this.url,this.data):this.url,i=this.createXhr();i.open(this.method,n,!0),this.withCredentials&&(i.withCredentials=!0),this.setXhrHeaders(i,this.headers),i.onreadystatechange=function(){return 4===i.readyState?0===i.status?void t(r.addRequestInfo(new Error("Unspecified client error."))):void("function"==typeof e&&e({status:i.status,headers:r.parseHeaders(i.getAllResponseHeaders()),text:i.responseText})):void 0},i.send("GET"!==this.method&&JSON.stringify(this.data))},t.prototype.setXhrHeaders=function(e,t){Object.keys(t).forEach(function(r){e.setRequestHeader(r,t[r])})},t.prototype.createXhr=function(){try{return new XMLHttpRequest}catch(e){throw new Error("Unsupported browser: Failed to create XHR object.")}},t.prototype.parseHeaders=function(e){var t=e.split(/\r?\n/);t.pop();for(var r={},n=0;n + SingleQuoted + XML + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AA_BB" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy> \ No newline at end of file diff --git a/js/BrowserHttp.browser.spec.js b/js/BrowserHttp.browser.spec.js new file mode 100644 index 0000000..c1586de --- /dev/null +++ b/js/BrowserHttp.browser.spec.js @@ -0,0 +1,17 @@ +/// +var BrowserHttp = require('./BrowserHttp'); +var runIsomorphicTests = require('./runIsomorphicTests'); +var TestHelpers = require('./TestHelpers'); +describe('BrowserHttp.request()', function () { + var request = BrowserHttp.request; + runIsomorphicTests(request); + it('rejects a client error', function (done) { + var options = { + url: 'http://foo.bar.baz/qux' + }; + request(options, TestHelpers.noop, function (err) { + expect(err.message).toEqual('Unspecified client error.'); + done(); + }); + }); +}); diff --git a/js/BrowserHttp.js b/js/BrowserHttp.js new file mode 100644 index 0000000..2684ff1 --- /dev/null +++ b/js/BrowserHttp.js @@ -0,0 +1,81 @@ +/* istanbul ignore next: TypeScript extend */ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var Helpers = require('./Helpers'); +var IsoHttp = require('./IsoHttp'); +var BrowserHttp; +(function (BrowserHttp) { + function request(options, resolve, reject) { + new Agent(options).send(resolve, reject); + } + BrowserHttp.request = request; + var Agent = (function (_super) { + __extends(Agent, _super); + function Agent(options) { + _super.call(this, options); + var xrw = 'x-requested-with'; + this.headers[xrw] = this.headers[xrw] || 'XMLHttpRequest'; + } + Agent.prototype.send = function (resolve, reject) { + var _this = this; + var url = (this.method === 'GET') ? Helpers.joinUrlWithQuery(this.url, this.data) : this.url; + var xhr = this.createXhr(); + xhr.open(this.method, url, true); + if (this.withCredentials) { + xhr.withCredentials = true; + } + this.setXhrHeaders(xhr, this.headers); + xhr.onreadystatechange = function () { + if (xhr.readyState !== 4) { + return; + } + if (xhr.status === 0) { + reject(_this.addRequestInfo(new Error('Unspecified client error.'))); + return; + } + if (typeof resolve !== 'function') { + return; + } + resolve({ + status: xhr.status, + headers: _this.parseHeaders(xhr.getAllResponseHeaders()), + text: xhr.responseText + }); + }; + xhr.send(this.method !== 'GET' && JSON.stringify(this.data)); + }; + Agent.prototype.setXhrHeaders = function (xhr, headers) { + Object.keys(headers).forEach(function (name) { + xhr.setRequestHeader(name, headers[name]); + }); + }; + Agent.prototype.createXhr = function () { + try { + return new XMLHttpRequest(); + } + catch (err) { + throw new Error('Unsupported browser: Failed to create XHR object.'); + } + }; + Agent.prototype.parseHeaders = function (headers) { + var lines = headers.split(/\r?\n/); + lines.pop(); // final newline + var fields = {}; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var pos = line.indexOf(':'); + var field = line.slice(0, pos).toLowerCase(); + var value = line.slice(pos + 1).trim(); + fields[field] = value; + } + return fields; + }; + return Agent; + })(IsoHttp.Agent); + BrowserHttp.Agent = Agent; +})(BrowserHttp || (BrowserHttp = {})); +module.exports = BrowserHttp; diff --git a/js/FakeHttp.js b/js/FakeHttp.js new file mode 100644 index 0000000..2d0b591 --- /dev/null +++ b/js/FakeHttp.js @@ -0,0 +1,39 @@ +/* istanbul ignore next: TypeScript extend */ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var IsoHttp = require('./IsoHttp'); +var FakeHttp; +(function (FakeHttp) { + function request(options, resolve, reject) { + return new Agent(options).send(resolve, reject); + } + FakeHttp.request = request; + var Agent = (function (_super) { + __extends(Agent, _super); + function Agent() { + _super.apply(this, arguments); + } + Agent.prototype.send = function (resolve, reject) { + this.resolve = resolve; + this.reject = reject; + return this; + }; + Agent.prototype.respondWith = function (response) { + if (typeof this.resolve === 'function') { + this.resolve(response); + } + }; + Agent.prototype.rejectWith = function (err) { + if (typeof this.reject === 'function') { + this.reject(this.addRequestInfo(err)); + } + }; + return Agent; + })(IsoHttp.Agent); + FakeHttp.Agent = Agent; +})(FakeHttp || (FakeHttp = {})); +module.exports = FakeHttp; diff --git a/js/FakeHttp.spec.js b/js/FakeHttp.spec.js new file mode 100644 index 0000000..1338020 --- /dev/null +++ b/js/FakeHttp.spec.js @@ -0,0 +1,41 @@ +/// +var FakeHttp = require('./FakeHttp'); +var TestHelpers = require('./TestHelpers'); +describe('FakeHttp.request()', function () { + var request = FakeHttp.request; + var options = { + method: 'foo', + url: 'bar' + }; + it('returns an instance of FakeHttp.Agent', function () { + var req = request(options); + expect(req instanceof FakeHttp.Agent).toBe(true); + }); + it('fakes a resolved HTTP response with resolveWith', function () { + var fakeResponse = { + status: 123, + headers: { foo: 'bar' }, + text: 'baz' + }; + var req = request(options, function (response) { + expect(response).toEqual(fakeResponse); + }); + req.respondWith(fakeResponse); + }); + it('fakes a client-rejected HTTP request with rejectWith', function () { + var req = request(options, TestHelpers.noop, function (err) { + expect(err.method).toEqual('FOO'); + expect(err.url).toEqual('bar'); + expect(err.message).toEqual('baz'); + }); + req.rejectWith(new Error('baz')); + }); + it('ignores non-function callback args', function () { + var fn = function () { + var req = request(options, null, null); + req.respondWith(null); + req.rejectWith(null); + }; + expect(fn).not.toThrowError(); + }); +}); diff --git a/js/Helpers.js b/js/Helpers.js new file mode 100644 index 0000000..77b560c --- /dev/null +++ b/js/Helpers.js @@ -0,0 +1,14 @@ +function joinUrlWithQuery(url, query) { + if (!query || !Object.keys(query).length) { + return url; + } + var joiner = (url.indexOf('?') > -1) ? '&' : '?'; + return url + joiner + serializeObject(query); +} +exports.joinUrlWithQuery = joinUrlWithQuery; +function serializeObject(obj) { + return Object.keys(obj).map(function (key) { + return key + '=' + encodeURIComponent(obj[key]); + }).join('&'); +} +exports.serializeObject = serializeObject; diff --git a/js/Helpers.spec.js b/js/Helpers.spec.js new file mode 100644 index 0000000..a5093f9 --- /dev/null +++ b/js/Helpers.spec.js @@ -0,0 +1,40 @@ +/// +var Helpers = require('./Helpers'); +describe('Helpers', function () { + describe('joinUrlWithQuery', function () { + it('returns the URL as-is when the query is not supplied or empty', function () { + expect(Helpers.joinUrlWithQuery('foo')).toEqual('foo'); + expect(Helpers.joinUrlWithQuery('foo', {})).toEqual('foo'); + }); + it('joins the url and query with "?" if no "?" is found', function () { + var actual = Helpers.joinUrlWithQuery('foo', { bar: 'baz' }); + var expected = 'foo?bar=baz'; + expect(actual).toEqual(expected); + }); + it('joins the url and query with "&" if "?" is found', function () { + var actual = Helpers.joinUrlWithQuery('foo?bar=baz', { qux: 'quux' }); + var expected = 'foo?bar=baz&qux=quux'; + expect(actual).toEqual(expected); + }); + }); + describe('serializeObject', function () { + it('joins keys and values with "="', function () { + var actual = Helpers.serializeObject({ foo: 'bar' }); + var expected = 'foo=bar'; + expect(actual).toEqual(expected); + }); + it('joins props with "&"', function () { + var actual = Helpers.serializeObject({ + foo: 'bar', + baz: 'qux' + }); + var expected = 'foo=bar&baz=qux'; + expect(actual).toEqual(expected); + }); + it('encodes the value with encodeURIComponent', function () { + var actual = Helpers.serializeObject({ foo: 'bar baz' }); + var expected = 'foo=bar%20baz'; + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/js/IsoHttp.js b/js/IsoHttp.js new file mode 100644 index 0000000..91e9167 --- /dev/null +++ b/js/IsoHttp.js @@ -0,0 +1,52 @@ +var IsoHttp; +(function (IsoHttp) { + var Agent = (function () { + function Agent(options) { + this.headers = {}; + if (!options || !Object.keys(options).length) { + throw new Error('Missing options.'); + } + if (!options.url) { + throw new Error('Missing required option: url.'); + } + this.url = options.url; + this.method = (options.method || 'GET').toUpperCase(); + if (options.headers) { + this.setHeaders(options.headers); + } + if (options.contentType) { + this.contentType = options.contentType; + } + this.withCredentials = options.withCredentials || false; + this.data = options.data || {}; + } + Object.defineProperty(Agent.prototype, "contentType", { + get: function () { + return this.headers['content-type']; + }, + set: function (value) { + this.headers['content-type'] = value; + }, + enumerable: true, + configurable: true + }); + Agent.prototype.setHeaders = function (headers) { + var _this = this; + Object.keys(headers).forEach(function (fieldName) { + _this.headers[fieldName] = headers[fieldName]; + }); + }; + Agent.prototype.send = function (resolve, reject) { + throw new Error('Not implemented.'); + }; + Agent.prototype.addRequestInfo = function (err) { + var result = err; + result.method = this.method; + result.url = this.url; + return result; + }; + return Agent; + })(); + IsoHttp.Agent = Agent; +})(IsoHttp || (IsoHttp = {})); +module.exports = IsoHttp; diff --git a/js/IsoHttp.spec.js b/js/IsoHttp.spec.js new file mode 100644 index 0000000..cbddf5e --- /dev/null +++ b/js/IsoHttp.spec.js @@ -0,0 +1,47 @@ +/// +/// +var IsoHttp = require('./IsoHttp'); +describe('IsoHttp.Agent constructor', function () { + // ReSharper disable once InconsistentNaming + var Agent = IsoHttp.Agent; + it('errors if no options are supplied', function () { + var fn = function () { + return new Agent(); + }; + expect(fn).toThrowError('Missing options.'); + fn = function () { + return new Agent({}); + }; + expect(fn).toThrowError('Missing options.'); + }); + it('errors if no url is supplied as an option', function () { + var fn = function () { + return new Agent({ foo: 'bar' }); + }; + expect(fn).toThrowError('Missing required option: url.'); + }); + it('errors when the send method is not implemented', function () { + var fn = function () { + new Agent({ url: 'foo' }).send(); + }; + expect(fn).toThrowError('Not implemented.'); + }); + it('sets headers', function () { + var headers = { + bar: 'baz', + qux: 'quux' + }; + var agent = new Agent({ + url: 'foo', + headers: headers + }); + expect(agent.headers).toEqual(headers); + }); + it('sets Content-Type', function () { + var agent = new Agent({ + url: 'foo', + contentType: 'bar' + }); + expect(agent.contentType).toEqual('bar'); + }); +}); diff --git a/js/NodeHttp.js b/js/NodeHttp.js new file mode 100644 index 0000000..05c95cd --- /dev/null +++ b/js/NodeHttp.js @@ -0,0 +1,61 @@ +/// +/* istanbul ignore next: TypeScript extend */ +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +var IsoHttp = require('./IsoHttp'); +var http = require('http'); +var url = require('url'); +var NodeHttp; +(function (NodeHttp) { + function request(options, resolve, reject) { + new Agent(options).send(resolve, reject); + } + NodeHttp.request = request; + var Agent = (function (_super) { + __extends(Agent, _super); + function Agent() { + _super.apply(this, arguments); + } + Agent.prototype.send = function (resolve, reject) { + var _this = this; + var parsedUrl = url.parse(this.url); + var options = { + host: parsedUrl.host, + hostname: parsedUrl.hostname, + method: this.method, + path: parsedUrl.path, + headers: this.headers, + }; + /* istanbul ignore else */ + if (parsedUrl.port) { + options.port = parseInt(parsedUrl.port, 10); + } + var req = http.request(options, function (response) { + if (typeof resolve !== 'function') { + return; + } + response.setEncoding('utf8'); + response.on('data', function (text) { + resolve({ + status: response.statusCode, + headers: response.headers, + text: text + }); + }); + }); + if (typeof reject === 'function') { + req.on('error', function (err) { + reject(_this.addRequestInfo(err)); + }); + } + req.end(); + }; + return Agent; + })(IsoHttp.Agent); + NodeHttp.Agent = Agent; +})(NodeHttp || (NodeHttp = {})); +module.exports = NodeHttp; diff --git a/js/NodeHttp.spec.js b/js/NodeHttp.spec.js new file mode 100644 index 0000000..15c3853 --- /dev/null +++ b/js/NodeHttp.spec.js @@ -0,0 +1,18 @@ +/// +/// +var NodeHttp = require('./NodeHttp'); +var runIsomorphicTests = require('./runIsomorphicTests'); +var TestHelpers = require('./TestHelpers'); +describe('NodeHttp.request()', function () { + var request = NodeHttp.request; + runIsomorphicTests(request); + it('rejects a client error', function (done) { + var options = { + url: 'http://foo.bar.baz/qux' + }; + request(options, TestHelpers.noop, function (err) { + expect(err.message).toEqual('getaddrinfo ENOTFOUND'); + done(); + }); + }); +}); diff --git a/js/TestHelpers.js b/js/TestHelpers.js new file mode 100644 index 0000000..591013e --- /dev/null +++ b/js/TestHelpers.js @@ -0,0 +1,16 @@ +function getApiPath(path) { + return 'http://localhost:3000' + path; +} +exports.getApiPath = getApiPath; +function isPlainObject(obj) { + if (typeof obj === 'object' && obj) { + return obj.constructor === Object; + } + return false; +} +exports.isPlainObject = isPlainObject; +/* istanbul ignore next: function not covered */ +function noop() { + // noop +} +exports.noop = noop; diff --git a/js/TestHelpers.spec.js b/js/TestHelpers.spec.js new file mode 100644 index 0000000..7ebb759 --- /dev/null +++ b/js/TestHelpers.spec.js @@ -0,0 +1,29 @@ +/// +var TestHelpers = require('./TestHelpers'); +describe('TestHelpers', function () { + describe('getApiPath()', function () { + var getApiPath = TestHelpers.getApiPath; + it('prepends a string with http://localhost:3000', function () { + expect(getApiPath('/foo')).toEqual('http://localhost:3000/foo'); + }); + }); + describe('isPlainObject()', function () { + var isPlainObject = TestHelpers.isPlainObject; + it('validates an object literal', function () { + expect(isPlainObject({})).toBe(true); + }); + it('validates a newwed-up Object', function () { + expect(isPlainObject(new Object())).toBe(true); + }); + it('invalidates other object types', function () { + expect(isPlainObject([])).toBe(false); + expect(isPlainObject(new Error())).toBe(false); + expect(isPlainObject('')).toBe(false); + expect(isPlainObject(42)).toBe(false); + /* istanbul ignore next: function not covered */ + expect(isPlainObject(function () { + // noop + })).toBe(false); + }); + }); +}); diff --git a/js/Types.js b/js/Types.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/js/Types.js @@ -0,0 +1 @@ + diff --git a/js/http.browser.js b/js/http.browser.js deleted file mode 100644 index b8b5ec1..0000000 --- a/js/http.browser.js +++ /dev/null @@ -1,6 +0,0 @@ -var Http = (function () { - function Http() { - } - return Http; -})(); -exports.Http = Http; diff --git a/js/http.browser.spec.js b/js/http.browser.spec.js deleted file mode 100644 index e976a50..0000000 --- a/js/http.browser.spec.js +++ /dev/null @@ -1,6 +0,0 @@ -/// -describe('http module (browser)', function () { - it('does foo', function () { - expect(true).toBe(true); - }); -}); diff --git a/js/http.js b/js/http.js deleted file mode 100644 index b8b5ec1..0000000 --- a/js/http.js +++ /dev/null @@ -1,6 +0,0 @@ -var Http = (function () { - function Http() { - } - return Http; -})(); -exports.Http = Http; diff --git a/js/http.spec.js b/js/http.spec.js deleted file mode 100644 index a25d610..0000000 --- a/js/http.spec.js +++ /dev/null @@ -1,6 +0,0 @@ -/// -describe('http module', function () { - it('does foo', function () { - expect(true).toBe(true); - }); -}); diff --git a/js/runIsomorphicTests.js b/js/runIsomorphicTests.js new file mode 100644 index 0000000..d097d57 --- /dev/null +++ b/js/runIsomorphicTests.js @@ -0,0 +1,64 @@ +/// +var TestHelpers = require('./TestHelpers'); +function runIsomorphicTests(request) { + it('handles a 200', function (done) { + var options = { + url: TestHelpers.getApiPath('/foo') + }; + request(options, function (response) { + expect(response.status).toEqual(200); + expect(response.text).toEqual('bar'); + done(); + }); + }); + it('handles a 200 w/o a resolve callback', function () { + var options = { + url: TestHelpers.getApiPath('/foo') + }; + var fn = function () { + request(options); + }; + expect(fn).not.toThrowError(); + }); + it('handles a 500', function (done) { + var options = { + url: TestHelpers.getApiPath('/error') + }; + request(options, function (response) { + expect(response.status).toEqual(500); + expect(response.text).toEqual('fail'); + done(); + }); + }); + it('handles a 404', function (done) { + var options = { + url: TestHelpers.getApiPath('/not-found') + }; + request(options, function (response) { + expect(response.status).toEqual(404); + done(); + }); + }); + it('responds with headers as an object literal', function (done) { + var options = { + url: TestHelpers.getApiPath('/not-found') + }; + request(options, function (response) { + expect(TestHelpers.isPlainObject(response.headers)).toBe(true); + done(); + }); + }); + it('rejects a client error', function (done) { + var options = { + url: 'http://foo.bar.baz/qux' + }; + request(options, TestHelpers.noop, function (err) { + expect(err instanceof Error).toBe(true); + expect(err.method).toEqual('GET'); + expect(err.url).toEqual('http://foo.bar.baz/qux'); + done(); + }); + }); +} +; +module.exports = runIsomorphicTests; diff --git a/js/server.js b/js/server.js new file mode 100644 index 0000000..c70dfc8 --- /dev/null +++ b/js/server.js @@ -0,0 +1,16 @@ +/// +var cors = require('cors'); +var express = require('express'); +var app = express(); +app.set('json spaces', 0); +app.use(cors()); +app.get('/foo', function (request, response) { + response.status(200).send('bar'); +}); +app.get('/error', function (request, response) { + response.status(500).send('fail'); +}); +var server = app.listen(3000, function () { + console.log('Test server listening on port %d', server.address().port); +}); +module.exports = server; diff --git a/karma.conf.js b/karma.conf.js index 69392f7..260abbf 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -10,12 +10,12 @@ module.exports = function(config) { // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine'], + frameworks: ['browserify', 'jasmine'], // list of files / patterns to load in the browser files: [ - 'js/lib/**/*.spec.js' + 'js/**/*.browser.spec.js' ], @@ -27,6 +27,7 @@ module.exports = function(config) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { + 'js/**/*.browser.spec.js': ['browserify'] }, @@ -55,16 +56,23 @@ module.exports = function(config) { // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: [ - 'PhantomJS' -// 'Chrome', -// 'Firefox', -// 'Safari', -// 'Opera', -// 'IE' + browsers: process.env.TRAVIS ? ['Chrome_travis_ci', 'Firefox'] : [ + 'Chrome', + 'ChromeCanary', + 'Firefox', + 'Safari', + 'IE' ], + customLaunchers: { + Chrome_travis_ci: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, + + // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: false diff --git a/lib/BrowserHttp.browser.spec.ts b/lib/BrowserHttp.browser.spec.ts new file mode 100644 index 0000000..a63618d --- /dev/null +++ b/lib/BrowserHttp.browser.spec.ts @@ -0,0 +1,23 @@ +/// + +import BrowserHttp = require('./BrowserHttp'); +import runIsomorphicTests = require('./runIsomorphicTests'); +import TestHelpers = require('./TestHelpers'); + +describe('BrowserHttp.request()', () => { + + var request = BrowserHttp.request; + + runIsomorphicTests(request); + + it('rejects a client error', done => { + var options = { + url: 'http://foo.bar.baz/qux' + }; + request(options, TestHelpers.noop, err => { + expect(err.message).toEqual('Unspecified client error.'); + done(); + }); + }); + +}); diff --git a/lib/BrowserHttp.ts b/lib/BrowserHttp.ts new file mode 100644 index 0000000..e1386ad --- /dev/null +++ b/lib/BrowserHttp.ts @@ -0,0 +1,84 @@ +import Helpers = require('./Helpers'); +import IsoHttp = require('./IsoHttp'); +import Types = require('./Types'); + +module BrowserHttp { + + export function request( + options: IsoHttp.RequestOptions, + resolve?: IsoHttp.ResolveCallback, + reject?: IsoHttp.RejectCallback) { + new Agent(options).send(resolve, reject); + } + + export class Agent extends IsoHttp.Agent { + + constructor(options?: IsoHttp.RequestOptions) { + super(options); + var xrw = 'x-requested-with'; + this.headers[xrw] = this.headers[xrw] || 'XMLHttpRequest'; + } + + send(resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback) { + var url = (this.method === 'GET') + ? Helpers.joinUrlWithQuery(this.url, this.data) + : this.url; + var xhr = this.createXhr(); + xhr.open(this.method, url, true); + if (this.withCredentials) { + xhr.withCredentials = true; + } + this.setXhrHeaders(xhr, this.headers); + xhr.onreadystatechange = () => { + if (xhr.readyState !== 4) { + return; + } + if (xhr.status === 0) { + reject(this.addRequestInfo(new Error('Unspecified client error.'))); + return; + } + if (typeof resolve !== 'function') { + return; + } + resolve({ + status: xhr.status, + headers: this.parseHeaders(xhr.getAllResponseHeaders()), + text: xhr.responseText + }); + }; + xhr.send(this.method !== 'GET' && JSON.stringify(this.data)); + } + + private setXhrHeaders(xhr: XMLHttpRequest, headers: Types.HashTable) { + Object.keys(headers).forEach(name => { + xhr.setRequestHeader(name, headers[name]); + }); + } + + private createXhr(): XMLHttpRequest { + try { + return new XMLHttpRequest(); + } catch (err) { + throw new Error('Unsupported browser: Failed to create XHR object.'); + } + } + + private parseHeaders(headers: string) { + var lines = headers.split(/\r?\n/); + lines.pop(); // final newline + var fields: Types.HashTable = {}; + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + var pos = line.indexOf(':'); + var field = line.slice(0, pos).toLowerCase(); + var value = line.slice(pos + 1).trim(); + fields[field] = value; + } + return fields; + } + + } + +} + +export = BrowserHttp; diff --git a/lib/FakeHttp.spec.ts b/lib/FakeHttp.spec.ts new file mode 100644 index 0000000..87d9ece --- /dev/null +++ b/lib/FakeHttp.spec.ts @@ -0,0 +1,50 @@ +/// + +import FakeHttp = require('./FakeHttp'); +import IsoHttp = require('./IsoHttp'); +import TestHelpers = require('./TestHelpers'); + +describe('FakeHttp.request()', () => { + + var request = FakeHttp.request; + var options = { + method: 'foo', + url: 'bar' + }; + + it('returns an instance of FakeHttp.Agent', () => { + var req = request(options); + expect(req instanceof FakeHttp.Agent).toBe(true); + }); + + it('fakes a resolved HTTP response with resolveWith', () => { + var fakeResponse: IsoHttp.Response = { + status: 123, + headers: { foo: 'bar' }, + text: 'baz' + }; + var req = request(options, response => { + expect(response).toEqual(fakeResponse); + }); + req.respondWith(fakeResponse); + }); + + it('fakes a client-rejected HTTP request with rejectWith', () => { + var req = request(options, TestHelpers.noop, err => { + expect(err.method).toEqual('FOO'); + expect(err.url).toEqual('bar'); + expect(err.message).toEqual('baz'); + }); + req.rejectWith(new Error('baz')); + }); + + it('ignores non-function callback args', () => { + var fn = () => { + var req = request(options, null, null); + req.respondWith(null); + req.rejectWith(null); + }; + expect(fn).not.toThrowError(); + }); + +}); diff --git a/lib/FakeHttp.ts b/lib/FakeHttp.ts new file mode 100644 index 0000000..0d55c8d --- /dev/null +++ b/lib/FakeHttp.ts @@ -0,0 +1,39 @@ +import IsoHttp = require('./IsoHttp'); + +module FakeHttp { + + export function request( + options?: IsoHttp.RequestOptions, + resolve?: IsoHttp.ResolveCallback, + reject?: IsoHttp.RejectCallback) { + return new Agent(options).send(resolve, reject); + } + + export class Agent extends IsoHttp.Agent { + + private resolve: IsoHttp.ResolveCallback; + private reject: IsoHttp.RejectCallback; + + send(resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback) { + this.resolve = resolve; + this.reject = reject; + return this; + } + + respondWith(response: IsoHttp.Response) { + if (typeof this.resolve === 'function') { + this.resolve(response); + } + } + + rejectWith(err: Error) { + if (typeof this.reject === 'function') { + this.reject(this.addRequestInfo(err)); + } + } + + } + +} + +export = FakeHttp; diff --git a/lib/Helpers.spec.ts b/lib/Helpers.spec.ts new file mode 100644 index 0000000..52a7726 --- /dev/null +++ b/lib/Helpers.spec.ts @@ -0,0 +1,53 @@ +/// + +import Helpers = require('./Helpers'); + +describe('Helpers', () => { + + describe('joinUrlWithQuery', () => { + + it('returns the URL as-is when the query is not supplied or empty', () => { + expect(Helpers.joinUrlWithQuery('foo')).toEqual('foo'); + expect(Helpers.joinUrlWithQuery('foo', {})).toEqual('foo'); + }); + + it('joins the url and query with "?" if no "?" is found', () => { + var actual = Helpers.joinUrlWithQuery('foo', { bar: 'baz' }); + var expected = 'foo?bar=baz'; + expect(actual).toEqual(expected); + }); + + it('joins the url and query with "&" if "?" is found', () => { + var actual = Helpers.joinUrlWithQuery('foo?bar=baz', { qux: 'quux' }); + var expected = 'foo?bar=baz&qux=quux'; + expect(actual).toEqual(expected); + }); + + }); + + describe('serializeObject', () => { + + it('joins keys and values with "="', () => { + var actual = Helpers.serializeObject({ foo: 'bar' }); + var expected = 'foo=bar'; + expect(actual).toEqual(expected); + }); + + it('joins props with "&"', () => { + var actual = Helpers.serializeObject({ + foo: 'bar', + baz: 'qux' + }); + var expected = 'foo=bar&baz=qux'; + expect(actual).toEqual(expected); + }); + + it('encodes the value with encodeURIComponent', () => { + var actual = Helpers.serializeObject({ foo: 'bar baz' }); + var expected = 'foo=bar%20baz'; + expect(actual).toEqual(expected); + }); + + }); + +}); diff --git a/lib/Helpers.ts b/lib/Helpers.ts new file mode 100644 index 0000000..197e5de --- /dev/null +++ b/lib/Helpers.ts @@ -0,0 +1,13 @@ +export function joinUrlWithQuery(url: string, query?: {}) { + if (!query || !Object.keys(query).length) { + return url; + } + var joiner = (url.indexOf('?') > -1) ? '&' : '?'; + return url + joiner + serializeObject(query); +} + +export function serializeObject(obj: {}) { + return Object.keys(obj).map(key => { + return key + '=' + encodeURIComponent(obj[key]); + }).join('&'); +} diff --git a/lib/IsoHttp.spec.ts b/lib/IsoHttp.spec.ts new file mode 100644 index 0000000..253e833 --- /dev/null +++ b/lib/IsoHttp.spec.ts @@ -0,0 +1,56 @@ +/// +/// + +import IsoHttp = require('./IsoHttp'); + +describe('IsoHttp.Agent constructor', () => { + + // ReSharper disable once InconsistentNaming + var Agent = IsoHttp.Agent; + + it('errors if no options are supplied', () => { + var fn = () => { + return new Agent(); + }; + expect(fn).toThrowError('Missing options.'); + fn = () => { + return new Agent({}); + }; + expect(fn).toThrowError('Missing options.'); + }); + + it('errors if no url is supplied as an option', () => { + var fn = () => { + return new Agent({ foo: 'bar' }); + }; + expect(fn).toThrowError('Missing required option: url.'); + }); + + it('errors when the send method is not implemented', () => { + var fn = () => { + new Agent({ url: 'foo' }).send(); + }; + expect(fn).toThrowError('Not implemented.'); + }); + + it('sets headers', () => { + var headers = { + bar: 'baz', + qux: 'quux' + }; + var agent = new Agent({ + url: 'foo', + headers: headers + }); + expect(agent.headers).toEqual(headers); + }); + + it('sets Content-Type', () => { + var agent = new Agent({ + url: 'foo', + contentType: 'bar' + }); + expect(agent.contentType).toEqual('bar'); + }); + +}); diff --git a/lib/IsoHttp.ts b/lib/IsoHttp.ts new file mode 100644 index 0000000..e5c2a55 --- /dev/null +++ b/lib/IsoHttp.ts @@ -0,0 +1,93 @@ +import Types = require('./Types'); + +module IsoHttp { + + export interface Request { + (options: RequestOptions, resolve?: ResolveCallback, reject?: RejectCallback): void; + } + + export interface ResolveCallback { + (response: Response): void; + } + + export interface RejectCallback { + (error: ClientError): void; + } + + export class Agent { + + protected url: string; + protected method: string; + protected headers: Types.HashTable = {}; + protected data: Types.HashTable; + protected withCredentials: boolean; + + get contentType() { + return this.headers['content-type']; + } + + set contentType(value: string) { + this.headers['content-type'] = value; + } + + constructor(options: RequestOptions) { + if (!options || !Object.keys(options).length) { + throw new Error('Missing options.'); + } + if (!options.url) { + throw new Error('Missing required option: url.'); + } + this.url = options.url; + this.method = (options.method || 'GET').toUpperCase(); + if (options.headers) { + this.setHeaders(options.headers); + } + if (options.contentType) { + this.contentType = options.contentType; + } + this.withCredentials = options.withCredentials || false; + this.data = options.data || {}; + } + + setHeaders(headers: Types.HashTable) { + Object.keys(headers).forEach(fieldName => { + this.headers[fieldName] = headers[fieldName]; + }); + } + + send(resolve?: ResolveCallback, reject?: RejectCallback): void { + throw new Error('Not implemented.'); + } + + protected addRequestInfo(err: Error): ClientError { + var result = err; + result.method = this.method; + result.url = this.url; + return result; + } + + } + + export interface Response { + headers: Types.HashTable; + status: number; + text: string; + } + + export interface RequestOptions { + contentType?: string; + data?: any; + headers?: any; + method?: string; + url?: string; + withCredentials?: boolean; + } + + export interface ClientError extends Error { + method: string; + url: string; + } + +} + +export = IsoHttp; diff --git a/lib/NodeHttp.spec.ts b/lib/NodeHttp.spec.ts new file mode 100644 index 0000000..a3712d3 --- /dev/null +++ b/lib/NodeHttp.spec.ts @@ -0,0 +1,24 @@ +/// +/// + +import NodeHttp = require('./NodeHttp'); +import runIsomorphicTests = require('./runIsomorphicTests'); +import TestHelpers = require('./TestHelpers'); + +describe('NodeHttp.request()', () => { + + var request = NodeHttp.request; + + runIsomorphicTests(request); + + it('rejects a client error', done => { + var options = { + url: 'http://foo.bar.baz/qux' + }; + request(options, TestHelpers.noop, err => { + expect(err.message).toEqual('getaddrinfo ENOTFOUND'); + done(); + }); + }); + +}); diff --git a/lib/NodeHttp.ts b/lib/NodeHttp.ts new file mode 100644 index 0000000..c0a9d8b --- /dev/null +++ b/lib/NodeHttp.ts @@ -0,0 +1,57 @@ +/// +/* istanbul ignore next: TypeScript extend */ + +import IsoHttp = require('./IsoHttp'); +import http = require('http'); +import url = require('url'); + +module NodeHttp { + + export function request( + options: IsoHttp.RequestOptions, + resolve?: IsoHttp.ResolveCallback, + reject?: IsoHttp.RejectCallback) { + new Agent(options).send(resolve, reject); + } + + export class Agent extends IsoHttp.Agent { + + send(resolve?: IsoHttp.ResolveCallback, reject?: IsoHttp.RejectCallback) { + var parsedUrl = url.parse(this.url); + var options: any = { + host: parsedUrl.host, + hostname: parsedUrl.hostname, + method: this.method, + path: parsedUrl.path, + headers: this.headers, + }; + /* istanbul ignore else */ + if (parsedUrl.port) { + options.port = parseInt(parsedUrl.port, 10); + } + var req = http.request(options, response => { + if (typeof resolve !== 'function') { + return; + } + response.setEncoding('utf8'); + response.on('data', text => { + resolve({ + status: response.statusCode, + headers: response.headers, + text: text + }); + }); + }); + if (typeof reject === 'function') { + req.on('error', err => { + reject(this.addRequestInfo(err)); + }); + } + req.end(); + } + + } + +} + +export = NodeHttp; diff --git a/lib/TestHelpers.spec.ts b/lib/TestHelpers.spec.ts new file mode 100644 index 0000000..e4059b1 --- /dev/null +++ b/lib/TestHelpers.spec.ts @@ -0,0 +1,42 @@ +/// + +import TestHelpers = require('./TestHelpers'); + +describe('TestHelpers', () => { + + describe('getApiPath()', () => { + + var getApiPath = TestHelpers.getApiPath; + + it('prepends a string with http://localhost:3000', () => { + expect(getApiPath('/foo')).toEqual('http://localhost:3000/foo'); + }); + + }); + + describe('isPlainObject()', () => { + + var isPlainObject = TestHelpers.isPlainObject; + + it('validates an object literal', () => { + expect(isPlainObject({})).toBe(true); + }); + + it('validates a newwed-up Object', () => { + expect(isPlainObject(new Object())).toBe(true); + }); + + it('invalidates other object types', () => { + expect(isPlainObject([])).toBe(false); + expect(isPlainObject(new Error())).toBe(false); + expect(isPlainObject('')).toBe(false); + expect(isPlainObject(42)).toBe(false); + /* istanbul ignore next: function not covered */ + expect(isPlainObject(() => { + // noop + })).toBe(false); + }); + + }); + +}); diff --git a/lib/TestHelpers.ts b/lib/TestHelpers.ts new file mode 100644 index 0000000..bcc97f3 --- /dev/null +++ b/lib/TestHelpers.ts @@ -0,0 +1,15 @@ +export function getApiPath(path: string) { + return 'http://localhost:3000' + path; +} + +export function isPlainObject(obj: Object) { + if (typeof obj === 'object' && obj) { + return obj.constructor === Object; + } + return false; +} + +/* istanbul ignore next: function not covered */ +export function noop() { + // noop +} diff --git a/lib/Types.ts b/lib/Types.ts new file mode 100644 index 0000000..bdaa181 --- /dev/null +++ b/lib/Types.ts @@ -0,0 +1,3 @@ +export interface HashTable { + [key: string]: T; +} diff --git a/lib/http.browser.spec.ts b/lib/http.browser.spec.ts deleted file mode 100644 index 73f58c6..0000000 --- a/lib/http.browser.spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// - -describe('http module (browser)', () => { - - it('does foo', () => { - expect(true).toBe(true); - }); - -}); diff --git a/lib/http.browser.ts b/lib/http.browser.ts deleted file mode 100644 index 95a3679..0000000 --- a/lib/http.browser.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Http { - -} diff --git a/lib/http.spec.ts b/lib/http.spec.ts deleted file mode 100644 index f92cd5a..0000000 --- a/lib/http.spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -/// - -describe('http module', () => { - - it('does foo', () => { - expect(true).toBe(true); - }); - -}); diff --git a/lib/http.ts b/lib/http.ts deleted file mode 100644 index 95a3679..0000000 --- a/lib/http.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class Http { - -} diff --git a/lib/runIsomorphicTests.ts b/lib/runIsomorphicTests.ts new file mode 100644 index 0000000..3c78aad --- /dev/null +++ b/lib/runIsomorphicTests.ts @@ -0,0 +1,74 @@ +/// + +import IsoHttp = require('./IsoHttp'); +import TestHelpers = require('./TestHelpers'); + +function runIsomorphicTests(request: IsoHttp.Request) { + + it('handles a 200', done => { + var options = { + url: TestHelpers.getApiPath('/foo') + }; + request(options, response => { + expect(response.status).toEqual(200); + expect(response.text).toEqual('bar'); + done(); + }); + }); + + it('handles a 200 w/o a resolve callback', () => { + var options = { + url: TestHelpers.getApiPath('/foo') + }; + var fn = () => { + request(options); + }; + expect(fn).not.toThrowError(); + }); + + it('handles a 500', done => { + var options = { + url: TestHelpers.getApiPath('/error') + }; + request(options, response => { + expect(response.status).toEqual(500); + expect(response.text).toEqual('fail'); + done(); + }); + }); + + it('handles a 404', done => { + var options = { + url: TestHelpers.getApiPath('/not-found') + }; + request(options, response => { + expect(response.status).toEqual(404); + done(); + }); + }); + + it('responds with headers as an object literal', done => { + var options = { + url: TestHelpers.getApiPath('/not-found') + }; + request(options, response => { + expect(TestHelpers.isPlainObject(response.headers)).toBe(true); + done(); + }); + }); + + it('rejects a client error', done => { + var options = { + url: 'http://foo.bar.baz/qux' + }; + request(options, TestHelpers.noop, err => { + expect(err instanceof Error).toBe(true); + expect(err.method).toEqual('GET'); + expect(err.url).toEqual('http://foo.bar.baz/qux'); + done(); + }); + }); + +}; + +export = runIsomorphicTests; diff --git a/lib/server.ts b/lib/server.ts new file mode 100644 index 0000000..cb00686 --- /dev/null +++ b/lib/server.ts @@ -0,0 +1,23 @@ +/// +var cors = require('cors'); +var express = require('express'); + +var app = express(); + +app.set('json spaces', 0); + +app.use(cors()); + +app.get('/foo', (request, response) => { + response.status(200).send('bar'); +}); + +app.get('/error', (request, response) => { + response.status(500).send('fail'); +}); + +var server = app.listen(3000, () => { + console.log('Test server listening on port %d', server.address().port); +}); + +export = server; diff --git a/package.json b/package.json index 2c8d1b8..6fcb114 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "iso-http", "version": "0.0.0", "description": "An isomorphic HTTP request library.", - "main": "js/lib/http.js", + "main": "js/NodeHttp.js", "scripts": { "test": "gulp test" }, @@ -25,12 +25,13 @@ "homepage": "https://github.com/jedmao/iso-http", "devDependencies": { "browserify": "^6.3.3", + "cors": "^2.5.2", "del": "^0.1.3", "event-stream": "^3.1.7", + "express": "^4.10.4", "gulp": "^3.8.10", "gulp-istanbul": "^0.4.0", "gulp-jasmine": "^1.0.1", - "gulp-karma": "0.0.4", "gulp-plumber": "^0.6.6", "gulp-rename": "^1.2.0", "gulp-streamify": "0.0.5", @@ -39,8 +40,13 @@ "gulp-uglify": "^1.0.1", "jasmine-core": "^2.1.2", "karma": "^0.12.28", + "karma-browserify": "^1.0.0", + "karma-chrome-launcher": "^0.1.6", + "karma-firefox-launcher": "^0.1.3", + "karma-ie-launcher": "^0.1.5", "karma-jasmine": "^0.3.2", "karma-phantomjs-launcher": "^0.1.4", + "karma-safari-launcher": "^0.1.1", "through2": "^0.6.3", "vinyl-source-stream": "^1.0.0" } diff --git a/tasks/browserify.js b/tasks/browserify.js index f5f5f77..ae38330 100644 --- a/tasks/browserify.js +++ b/tasks/browserify.js @@ -7,7 +7,7 @@ var uglify = require('gulp-uglify'); module.exports = function() { return browserify() - .require('./js/http.browser.js', { expose: 'iso-http' }) + .require('./js/BrowserHttp.js', { expose: 'iso-http' }) .bundle() .pipe(source('iso-http.js')) .pipe(gulp.dest('dist')) diff --git a/tasks/test--watch.js b/tasks/test--watch.js deleted file mode 100644 index 043e9c8..0000000 --- a/tasks/test--watch.js +++ /dev/null @@ -1,12 +0,0 @@ -var gulp = require('gulp'); -var karma = require('gulp-karma'); -var plumber = require('gulp-plumber'); - -module.exports = function() { - return gulp.src('js/**/*.spec.js', { read: false }) - .pipe(plumber()) - .pipe(karma({ - configFile: 'karma.conf.js', - action: 'watch' - })); -}; diff --git a/tasks/test.js b/tasks/test.js index 0ed5b44..2339d57 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -1,20 +1,31 @@ var gulp = require('gulp'); var istanbul = require('gulp-istanbul'); -var karma = require('gulp-karma'); +var jasmine = require('gulp-jasmine'); +var karma = require('karma'); +var path = require('path'); var plumber = require('gulp-plumber'); -module.exports = function() { - return gulp.src('js/**/*.js') +module.exports = function(done) { + gulp.src('js/**/*.js') .pipe(istanbul()) .on('finish', function() { - gulp.src('js/**/*.spec.js', { read: false }) + var server = require('../js/server'); + gulp.src(['js/**/*.spec.js', '!js/**/*.browser.spec.js']) .pipe(plumber()) - .pipe(karma({ - configFile: 'karma.conf.js', - action: 'run' - })) - .pipe(istanbul.writeReports({ - reporters: ['lcov'] - })); + .pipe(jasmine()) + .on('finish', function() { + karma.server.start({ + configFile: path.resolve('karma.conf.js'), + singleRun: true + }, function(exitCode) { + console.log('Karma run has exited with ' + exitCode); + server.close(); + gulp.src('js/**/*.spec.js') + .pipe(istanbul.writeReports({ + reporters: ['lcov'] + })) + .on('finish', done); + }); + }); }); }; diff --git a/tasks/watch.js b/tasks/watch.js deleted file mode 100644 index 747f005..0000000 --- a/tasks/watch.js +++ /dev/null @@ -1,5 +0,0 @@ -var gulp = require('gulp'); - -module.exports = function() { - gulp.watch('lib/**/*.ts', ['test:onScriptsChanged']); -}; diff --git a/{ b/{ new file mode 100644 index 0000000..e69de29