diff --git a/koa/koa-test.ts b/koa/koa-test.ts new file mode 100644 index 0000000000..9edfbbaf5d --- /dev/null +++ b/koa/koa-test.ts @@ -0,0 +1,26 @@ +/// + +import * as Koa from 'koa'; +var app = new Koa(); + +// logger + +app.use(function *(next: Function) { + // at present, we can not typing 'this' in function + // but there is a proposal: https://github.com/Microsoft/TypeScript/issues/3694 + var ctx: Koa.Context = this; + + var start = new Date; + yield next; + var ms = new Date - start; + console.log('%s %s - %s', ctx.method, ctx.url, ms); +}); + +// response + +app.use(function *(): Iterable { + var ctx: Koa.Context = this; + ctx.body = 'Hello World'; +}); + +app.listen(3000); \ No newline at end of file diff --git a/koa/koa-test.ts.tscparams b/koa/koa-test.ts.tscparams new file mode 100644 index 0000000000..4a863174b8 --- /dev/null +++ b/koa/koa-test.ts.tscparams @@ -0,0 +1 @@ +--noImplicitAny --module commonjs --target es6 \ No newline at end of file diff --git a/koa/koa.d.ts b/koa/koa.d.ts new file mode 100644 index 0000000000..0e9c49f6bd --- /dev/null +++ b/koa/koa.d.ts @@ -0,0 +1,686 @@ +// Type definitions for koa v1.1.2 +// Project: https://github.com/koajs/koa +// Definitions by: jKey Lu +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/* =================== USAGE =================== + + import * as Koa from 'koa; + var app = new Koa(); + + =============================================== */ + +/// + +declare module "koa" { + import * as net from 'net'; + import * as http from 'http'; + import * as events from 'events'; + + // waiting https://github.com/DefinitelyTyped/DefinitelyTyped/pull/7311 be merged + // import * as Cookies from 'cookies'; + module Cookies { + interface ICookies { + /** + * This extracts the cookie with the given name from the + * Cookie header in the request. If such a cookie exists, + * its value is returned. Otherwise, nothing is returned. + */ + get(name: string): string; + /** + * This extracts the cookie with the given name from the + * Cookie header in the request. If such a cookie exists, + * its value is returned. Otherwise, nothing is returned. + */ + get(name: string, opts: IOptions): string; + + /** + * This sets the given cookie in the response and returns + * the current context to allow chaining.If the value is omitted, + * an outbound header with an expired date is used to delete the cookie. + */ + set(name: string, value: string): ICookies; + /** + * This sets the given cookie in the response and returns + * the current context to allow chaining.If the value is omitted, + * an outbound header with an expired date is used to delete the cookie. + */ + set(name: string, value: string, opts: IOptions): ICookies; + } + + interface IOptions { + /** + * a number representing the milliseconds from Date.now() for expiry + */ + maxAge?: number; + /** + * a Date object indicating the cookie's expiration + * date (expires at the end of session by default). + */ + expires?: Date; + /** + * a string indicating the path of the cookie (/ by default). + */ + path?: string; + /** + * a string indicating the domain of the cookie (no default). + */ + domain?: string; + /** + * a boolean indicating whether the cookie is only to be sent + * over HTTPS (false by default for HTTP, true by default for HTTPS). + */ + secure?: boolean; + /** + * a boolean indicating whether the cookie is only to be sent + * over HTTPS (use this if you handle SSL not in your node process). + */ + secureProxy?: boolean; + /** + * a boolean indicating whether the cookie is only to be sent over HTTP(S), + * and not made available to client JavaScript (true by default). + */ + httpOnly?: boolean; + /** + * a boolean indicating whether the cookie is to be signed (false by default). + * If this is true, another cookie of the same name with the .sig suffix + * appended will also be sent, with a 27-byte url-safe base64 SHA1 value + * representing the hash of cookie-name=cookie-value against the first Keygrip key. + * This signature key is used to detect tampering the next time a cookie is received. + */ + signed?: boolean; + /** + * a boolean indicating whether to overwrite previously set + * cookies of the same name (false by default). If this is true, + * all cookies set during the same request with the same + * name (regardless of path or domain) are filtered out of + * the Set-Cookie header when setting this cookie. + */ + overwrite?: boolean; + } + } + + interface IRequest { + /** + * Return request header. + * + * @readonly + */ + header: any; + /** + * Return request header, alias as request.header + * + * @readonly + */ + headers: any; + /** + * Get/Set request URL. + */ + url: string; + /** + * Get origin of URL. + */ + origin: string; + /** + * Get full request URL. + * + * @readonly + */ + href: string; + /** + * Get/Set request method. + */ + method: string; + /** + * Get request pathname. + * Set pathname, retaining the query-string when present. + */ + path: string; + /** + * Get parsed query-string. + * Set query-string as an object. + */ + query: any; + /** + * Get/Set query string. + */ + querystring: string; + /** + * Get the search string. Same as the querystring except it includes the leading ?. + * Set the search string. Same as response.querystring= but included for ubiquity. + */ + search: string; + /** + * Parse the "Host" header field host and support X-Forwarded-Host when a proxy is enabled. + * + * @readonly + */ + host: string; + /** + * Parse the "Host" header field hostname and support X-Forwarded-Host when a proxy is enabled. + * + * @readonly + */ + hostname: string; + /** + * Check if the request is fresh, aka + * Last-Modified and/or the ETag + * still match. + * + * @readonly + */ + fresh: boolean; + /** + * Check if the request is stale, aka + * "Last-Modified" and / or the "ETag" for the + * resource has changed. + * + * @readonly + */ + stale: boolean; + /** + * Check if the request is idempotent. + * + * @readonly + */ + idempotent: boolean; + /** + * Return the request socket. + * + * @readonly + */ + socket: net.Socket; + /** + * Return the protocol string "http" or "https" + * when requested with TLS. When the proxy setting + * is enabled the "X-Forwarded-Proto" header + * field will be trusted. If you're running behind + * a reverse proxy that supplies https for you this + * may be enabled. + * + * @readonly + */ + protocol: string; + /** + * Short-hand for: + * + * this.protocol == 'https' + * + * @readonly + */ + secure: boolean; + /** + * Return the remote address, or when + * `app.proxy` is `true` return + * the upstream addr. + * + * @readonly + */ + ip: string; + /** + * When `app.proxy` is `true`, parse + * the "X-Forwarded-For" ip address list. + * + * For example if the value were "client, proxy1, proxy2" + * you would receive the array `["client", "proxy1", "proxy2"]` + * where "proxy2" is the furthest down-stream. + * + * @readonly + */ + ips: string[]; + /** + * Return subdomains as an array. + * + * Subdomains are the dot-separated parts of the host before the main domain of + * the app. By default, the domain of the app is assumed to be the last two + * parts of the host. This can be changed by setting `app.subdomainOffset`. + * + * For example, if the domain is "tobi.ferrets.example.com": + * If `app.subdomainOffset` is not set, this.subdomains is `["ferrets", "tobi"]`. + * If `app.subdomainOffset` is 3, this.subdomains is `["tobi"]`. + * + * @readonly + */ + subdomains: string[]; + /** + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single mime type string + * such as "application/json", the extension name + * such as "json" or an array `["json", "html", "text/plain"]`. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + * // Accept: text/html + * this.accepts('html'); + * // => "html" + * + * // Accept: text/*, application/json + * this.accepts('html'); + * // => "html" + * this.accepts('text/html'); + * // => "text/html" + * this.accepts('json', 'text'); + * // => "json" + * this.accepts('application/json'); + * // => "application/json" + * + * // Accept: text/*, application/json + * this.accepts('image/png'); + * this.accepts('png'); + * // => undefined + * + * // Accept: text/*;q=.5, application/json + * this.accepts(['html', 'json']); + * this.accepts('html', 'json'); + * // => "json" + */ + accepts(...types: string[]): string|boolean|string[]; + accepts(types: string[]): string|boolean|string[]; + /** + * Return accepted encodings or best fit based on `encodings`. + * + * Given `Accept-Encoding: gzip, deflate` + * an array sorted by quality is returned: + * + * ['gzip', 'deflate'] + */ + acceptsEncodings(...encodings: string[]): string|string[]; + acceptsEncodings(encodings: string[]): string|string[]; + /** + * Return accepted charsets or best fit based on `charsets`. + * + * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5` + * an array sorted by quality is returned: + * + * ['utf-8', 'utf-7', 'iso-8859-1'] + */ + acceptsCharsets(...charsets: string[]): string|string[]; + acceptsCharsets(charsets: string[]): string|string[]; + /** + * Return accepted languages or best fit based on `langs`. + * + * Given `Accept-Language: en;q=0.8, es, pt` + * an array sorted by quality is returned: + * + * ['es', 'pt', 'en'] + */ + acceptsLanguages(...langs: string[]): string|string[]; + acceptsLanguages(langs: string[]): string|string[]; + /** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains any of the give mime `type`s. + * If there is no request body, `null` is returned. + * If there is no content type, `false` is returned. + * Otherwise, it returns the first `type` that matches. + * + * Examples: + * + * // With Content-Type: text/html; charset=utf-8 + * this.is('html'); // => 'html' + * this.is('text/html'); // => 'text/html' + * this.is('text/*', 'application/json'); // => 'text/html' + * + * // When Content-Type is application/json + * this.is('json', 'urlencoded'); // => 'json' + * this.is('application/json'); // => 'application/json' + * this.is('html', 'application/*'); // => 'application/json' + * + * this.is('html'); // => false + */ + is(type: string): string|boolean; + is(types: string[]): string|boolean; + /** + * Return request header. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` are interchangeable. + * + * Examples: + * + * this.get('Content-Type'); + * // => "text/plain" + * + * this.get('content-type'); + * // => "text/plain" + * + * this.get('Something'); + * // => undefined + */ + get(field: string): string; + } + + interface IResponse { + /** + * Get/Set response status code. + */ + status: number; + /** + * Get/Set response status message + */ + message: string; + /** + * Get/Set response body. + */ + body: any; + /** + * Return parsed response Content-Length when present. + * Set Content-Length field to `n`. + */ + length: number; + /** + * Check if a header has been written to the socket. + * + * @readonly + */ + headerSent: boolean; + /** + * Vary on `field`. + */ + vary(field: string): void; + /** + * Perform a 302 redirect to `url`. + * + * The string "back" is special-cased + * to provide Referrer support, when Referrer + * is not present `alt` or "/" is used. + * + * Examples: + * + * this.redirect('back'); + * this.redirect('back', '/index.html'); + * this.redirect('/login'); + * this.redirect('http://google.com'); + */ + redirect(url: string): void; + redirect(url: string, alt: string): void; + /** + * Set Content-Disposition header to "attachment" with optional `filename`. + */ + attachment(filename: string): void; + /** + * Return the response mime type void of + * parameters such as "charset". + * + * Set Content-Type response header with `type` through `mime.lookup()` + * when it does not contain a charset. + * + * Examples: + * + * this.type = '.html'; + * this.type = 'html'; + * this.type = 'json'; + * this.type = 'application/json'; + * this.type = 'png'; + */ + type: string; + /** + * Get the Last-Modified date in Date form, if it exists. + * Set the Last-Modified date using a string or a Date. + * + * this.response.lastModified = new Date(); + * this.response.lastModified = '2013-09-13'; + */ + lastModified: Date; + /** + * Get the ETag of a response. + * Set the ETag of a response. + * This will normalize the quotes if necessary. + * + * this.response.etag = 'md5hashsum'; + * this.response.etag = '"md5hashsum"'; + * this.response.etag = 'W/"123456789"'; + */ + etag: string; + /** + * Set header `field` to `val`, or pass + * an object of header fields. + * + * Examples: + * + * this.set('Foo', ['bar', 'baz']); + * this.set('Accept', 'application/json'); + * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); + */ + set(field: any): void; + set(field: string, val: string): void; + set(field: string, val: any[]): void; + /** + * Append additional header `field` with value `val`. + * + * Examples: + * + * this.append('Link', ['', '']); + * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly'); + * this.append('Warning', '199 Miscellaneous warning'); + */ + append(field: string, val: string): void; + append(field: string, val: any[]): void; + /** + * Remove header `field`. + */ + remove(field: string): void; + /** + * Checks if the request is writable. + * Tests for the existence of the socket + * as node sometimes does not set it. + * + * @readonly + * @private + */ + writable: boolean; + } + + module koa { + interface BaseRequest extends IRequest { + /** + * Get the charset when present or undefined. + * + * @readonly + */ + charset: string; + /** + * Return parsed Content-Length when present. + * + * @readonly + */ + length: number; + /** + * Return the request mime type void of + * parameters such as "charset". + * + * @readonly + */ + type: string; + /** + * Inspect implementation. + */ + inspect(): any; + /** + * Return JSON representation. + */ + toJSON(): any; + } + + interface BaseResponse extends IResponse { + /** + * Return the request socket. + * + * @readonly + */ + socket: net.Socket; + /** + * Return response header. + * + * @readonly + */ + header: any; + /** + * Return response header, alias as response.header + */ + headers: any; + /** + * Check whether the response is one of the listed types. + * Pretty much the same as `this.request.is()`. + */ + is(type: string): string|boolean; + is(types: string[]): string|boolean; + /** + * Return response header. + * + * Examples: + * + * this.get('Content-Type'); + * // => "text/plain" + * + * this.get('content-type'); + * // => "text/plain" + */ + get(field: string): string; + /** + * Inspect implementation. + */ + inspect(): any; + /** + * Return JSON representation. + */ + toJSON(): any; + } + + interface BaseContext extends IRequest, IResponse { + /** + * util.inspect() implementation, which + * just returns the JSON output. + */ + inspect(): any; + /** + * Return JSON representation. + * + * Here we explicitly invoke .toJSON() on each + * object, as iteration will otherwise fail due + * to the getters and cause utilities such as + * clone() to fail. + */ + toJSON(): any; + /** + * Similar to .throw(), adds assertion. + * + * this.assert(this.user, 401, 'Please login!'); + * + * See: https://github.com/jshttp/http-assert + */ + assert(test: any, status: number, message: string): void; + /** + * Throw an error with `msg` and optional `status` + * defaulting to 500. Note that these are user-level + * errors, and the message may be exposed to the client. + * + * this.throw(403) + * this.throw('name required', 400) + * this.throw(400, 'name required') + * this.throw('something exploded') + * this.throw(new Error('invalid'), 400); + * this.throw(400, new Error('invalid')); + * + * See: https://github.com/jshttp/http-errors + */ + throw(...args: any[]): void; + /** + * Default error handling. + * + * @private + */ + onerror(err: Error): void; + } + + interface Request extends BaseRequest { + app: Application; + ctx: Context; + response: Response; + req: http.IncomingMessage; + res: http.ServerResponse; + } + + interface Response extends BaseResponse { + app: Application; + ctx: Context; + request: Request; + req: http.IncomingMessage; + res: http.ServerResponse; + } + + interface Context extends BaseContext { + request: Request; + response: Response; + app: Application; + req: http.IncomingMessage; + res: http.ServerResponse; + originalUrl: string; + cookies: Cookies.ICookies; + accept: any; + state: any; + } + + interface Application extends events.EventEmitter { + env: string; + subdomainOffset: number; + middleware: any[]; + proxy: boolean; + context: BaseContext; + request: BaseRequest; + response: BaseResponse; + /** + * Shorthand for: + * + * http.createServer(app.callback()).listen(...) + */ + listen(port: number, hostname?: string, backlog?: number, callback?: Function): http.Server; + listen(port: number, hostname?: string, callback?: Function): http.Server; + listen(path: string, callback?: Function): http.Server; + listen(handle: any, listeningListener?: Function): http.Server; + /** + * Return JSON representation. + * We only bother showing settings. + */ + inspect(): any; + /** + * Return JSON representation. + * We only bother showing settings. + */ + toJSON(): any; + /** + * Use the given middleware `fn`. + */ + use(fn: Function): Application; + /** + * Return a request handler callback + * for node's native http server. + */ + callback(): (req: http.IncomingMessage, res: http.ServerResponse) => void; + /** + * Initialize a new context. + * + * @private + */ + createContext(req: http.IncomingMessage, res: http.ServerResponse): Context; + /** + * Default error handler. + * + * @private + */ + onerror(err: Error): void; + } + } + + interface KoaStatic { + new (): koa.Application; + (): koa.Application; + } + + var koa: KoaStatic; + + export = koa; +} \ No newline at end of file