From 0f18c11b0cbd633d99899eebf117f98e3ca578db Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 4 Jul 2017 21:48:06 -0700 Subject: [PATCH 1/8] Use endpoint. Reviewed by @tolmasky. --- Makefile | 2 +- index.html | 568 +++++++++++++++++++++-------------------------- options.js | 56 +++++ package.json | 4 +- views/index.jade | 9 +- 5 files changed, 320 insertions(+), 319 deletions(-) create mode 100644 options.js diff --git a/Makefile b/Makefile index a1b55ea..3344ad7 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ DOCS = public/index.md \ public/guide.md index.html: views/index.jade $(DOCS) - @./node_modules/.bin/jade $< -o . + @./node_modules/.bin/pug --obj options.js $< -o . public/index.md: curl -s $(RAW)/koajs/koa/master/docs/api/index.md \ diff --git a/index.html b/index.html index 97f609d..e1e3ca9 100644 --- a/index.html +++ b/index.html @@ -1,33 +1,27 @@ -Koa - next generation web framework for node.js
next generation web framework for node.js

Introduction

Koa is a new web framework designed by the team behind Express, +window.analytics.page();

next generation web framework for node.js

Introduction

Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation -for web applications and APIs. By leveraging async functions, Koa allows you +for web applications and APIs. Through leveraging generators Koa allows you to ditch callbacks and greatly increase error-handling. Koa does not bundle any -middleware within its core, and it provides an elegant suite of methods that make +middleware within core, and provides an elegant suite of methods that make writing servers fast and enjoyable.

Installation

Koa requires node v7.6.0 or higher for ES2015 and async function support.

You can quickly install a supported version of node with your favorite version manager:

-
$ nvm install 7
+
$ nvm install 7
 $ npm i koa
-$ node my-koa-app.js
-
-

Async Functions with Babel

-

To use async functions in Koa in versions of node < 7.6, we recommend using babel's require hook.

-
require('babel-register');
-// require the rest of the app that needs to be transpiled after the hook
-const app = require('./app');
-
-

To parse and transpile async functions, +$ node my-koa-app.js

Async Functions with Babel

+

To use async functions in Koa in versions of node < 7.6, we recommend using babel's require hook.

+
require('babel-core/register');
+// require the rest of the app that needs to be transpiled after the hook
+const app = require('./app');

To parse and transpile async functions, you should at a minimum have the transform-async-to-generator or transform-async-to-module-method plugins. For example, in your .babelrc file, you should have:

-
{
-  "plugins": ["transform-async-to-generator"]
-}
-
-

You can also use the env preset with a target option "node": "current" instead.

+
{
+  "plugins": ["transform-async-to-generator"]
+}

You can also use the env preset with a target option "node": "current" instead.

Application

A Koa application is an object containing an array of middleware functions which are composed and executed in a stack-like manner upon request. Koa is similar to many @@ -39,15 +33,32 @@

Application

among others. Despite supplying a reasonably large number of helpful methods Koa maintains a small footprint, as no middleware are bundled.

The obligatory hello world application:

-
const Koa = require('koa');
-const app = new Koa();
+
const Koa = require('koa');
+const app = new Koa();
 
-app.use(async ctx => {
-  ctx.body = 'Hello World';
-});
+app.use(ctx => {
+  ctx.body = 'Hello World';
+});
 
-app.listen(3000);
-
+app.listen(3000);

This example is actually live, and you can visit the displayed URL to see it in action. If you edit it above, you'll see the changes take place instantly.

Cascading

Koa middleware cascade in a more traditional way as you may be used to with similar tools - this was previously difficult to make user friendly with node's use of callbacks. @@ -60,34 +71,52 @@

Cascading

the function suspends and passes control to the next middleware defined. After there are no more middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.

-
const Koa = require('koa');
-const app = new Koa();
+
const Koa = require('koa');
+const app = new Koa();
+
+// x-response-time
+
+app.use(async function (ctx, next) {
+  const start = Date.now();
+  await next();
+  const ms = Date.now() - start;
+  ctx.set('X-Response-Time', `${ms}ms`);
+});
 
-// logger
+// logger
 
-app.use(async (ctx, next) => {
-  await next();
-  const rt = ctx.response.get('X-Response-Time');
-  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
-});
+app.use(async function (ctx, next) {
+  const start = Date.now();
+  await next();
+  const ms = Date.now() - start;
+  console.log(`${ctx.method} ${ctx.url} - ${ms}`);
+});
 
-// x-response-time
+// response
 
-app.use(async (ctx, next) => {
-  const start = Date.now();
-  await next();
-  const ms = Date.now() - start;
-  ctx.set('X-Response-Time', `${ms}ms`);
-});
+app.use(ctx => {
+  ctx.body = 'Hello World';
+});
 
-// response
+app.listen(3000);

This example is also live, by going to it in another tab you'll see the logs above.

Settings

Application settings are properties on the app instance, currently the following are supported:

@@ -97,34 +126,28 @@

Settings

  • app.subdomainOffset offset of .subdomains to ignore [2]
  • app.listen(...)

    -

    A Koa application is not a 1-to-1 representation of an HTTP server. +

    A Koa application is not a 1-to-1 representation of a HTTP server. One or more Koa applications may be mounted together to form larger applications with a single HTTP server.

    Create and return an HTTP server, passing the given arguments to Server#listen(). These arguments are documented on nodejs.org. The following is a useless Koa application bound to port 3000:

    -
    const Koa = require('koa');
    -const app = new Koa();
    -app.listen(3000);
    -
    -

    The app.listen(...) method is simply sugar for the following:

    -
    const http = require('http');
    -const Koa = require('koa');
    -const app = new Koa();
    -http.createServer(app.callback()).listen(3000);
    -
    -

    This means you can spin up the same application as both HTTP and HTTPS +

    const Koa = require('koa');
    +const app = new Koa();
    +app.listen(3000);

    The app.listen(...) method is simply sugar for the following:

    +
    const http = require('http');
    +const Koa = require('koa');
    +const app = new Koa();
    +http.createServer(app.callback()).listen(3000);

    This means you can spin up the same application as both HTTP and HTTPS or on multiple addresses:

    -
    const http = require('http');
    -const https = require('https');
    -const Koa = require('koa');
    -const app = new Koa();
    -http.createServer(app.callback()).listen(3000);
    -https.createServer(app.callback()).listen(3001);
    -
    -

    app.callback()

    +
    const http = require('http');
    +const https = require('https');
    +const Koa = require('koa');
    +const app = new Koa();
    +http.createServer(app.callback()).listen(3000);
    +https.createServer(app.callback()).listen(3001);

    app.callback()

    Return a callback function suitable for the http.createServer() method to handle a request. - You may also use this callback function to mount your Koa app in a + You may also use this callback function to mount your koa app in a Connect/Express app.

    app.use(function)

    Add the given middleware function to this application. See Middleware for @@ -134,45 +157,35 @@

    app.keys=

    These are passed to KeyGrip, however you may also pass your own KeyGrip instance. For example the following are acceptable:

    -
    app.keys = ['im a newer secret', 'i like turtle'];
    -app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
    -
    -

    These keys may be rotated and are used when signing cookies +

    app.keys = ['im a newer secret', 'i like turtle'];
    +app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

    These keys may be rotated and are used when signing cookies with the { signed: true } option:

    -
    ctx.cookies.set('name', 'tobi', { signed: true });
    -
    -

    app.context

    -

    app.context is the prototype from which ctx is created. +

    ctx.cookies.set('name', 'tobi', { signed: true });

    app.context

    +

    app.context is the prototype from which ctx is created from. You may add additional properties to ctx by editing app.context. This is useful for adding properties or methods to ctx to be used across your entire app, which may be more performant (no middleware) and/or easier (fewer require()s) at the expense of relying more on ctx, which could be considered an anti-pattern.

    For example, to add a reference to your database from ctx:

    -
    app.context.db = db();
    +
    app.context.db = db();
     
    -app.use(async ctx => {
    -  console.log(ctx.db);
    -});
    -
    -

    Note:

    +app.use(async (ctx) => { + console.log(ctx.db); +});

    Note:

    • Many properties on ctx are defined using getters, setters, and Object.defineProperty(). You can only edit these properties (not recommended) by using Object.defineProperty() on app.context. See https://github.com/koajs/koa/issues/652.
    • -
    • Mounted apps currently use their parent's ctx and settings. Thus, mounted apps are really just groups of middleware.
    • +
    • Mounted apps currently use its parent's ctx and settings. Thus, mounted apps are really just groups of middleware.

    Error Handling

    By default outputs all errors to stderr unless app.silent is true. - The default error handler also won't output errors when err.status is 404 or err.expose is true. + The default error handler also won't outputs errors when err.status is 404 or err.expose is true. To perform custom error-handling logic such as centralized logging you can add an "error" event listener:

    -
    app.on('error', err => {
    -  log.error('server error', err)
    -});
    -
    -

    If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

    -
    app.on('error', (err, ctx) => {
    -  log.error('server error', err, ctx)
    -});
    -
    -

    When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond +

    app.on('error', err =>
    +  log.error('server error', err)
    +);

    If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

    +
    app.on('error', (err, ctx) =>
    +  log.error('server error', err, ctx)
    +);

    When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond appropriately with a 500 "Internal Server Error". In either case an app-level "error" is emitted for logging purposes.

    Context

    @@ -185,13 +198,11 @@

    Error Handling

    A Context is created per request, and is referenced in middleware as the receiver, or the ctx identifier, as shown in the following snippet:

    -
    app.use(async ctx => {
    -  ctx; // is the Context
    -  ctx.request; // is a Koa Request
    -  ctx.response; // is a Koa Response
    -});
    -
    -

    Many of the context's accessors and methods simply delegate to their ctx.request or ctx.response +

    app.use(async (ctx, next) => {
    +  ctx; // is the Context
    +  ctx.request; // is a koa Request
    +  ctx.response; // is a koa Response
    +});

    Many of the context's accessors and methods simply delegate to their ctx.request or ctx.response equivalents for convenience, and are otherwise identical. For example ctx.type and ctx.length delegate to the response object, and ctx.path and ctx.method delegate to the request.

    API

    @@ -208,21 +219,19 @@

    ctx.res

  • res.end()
  • ctx.request

    -

    A Koa Request object.

    +

    A koa Request object.

    ctx.response

    -

    A Koa Response object.

    +

    A koa Response object.

    ctx.state

    The recommended namespace for passing information through middleware and to your frontend views.

    -
    ctx.state.user = await User.find(id);
    -
    -

    ctx.app

    +
    ctx.state.user = await User.find(id);

    ctx.app

    Application instance reference.

    ctx.cookies.get(name, [options])

    Get cookie name with options:

    • signed the cookie requested should be signed
    -

    Koa uses the cookies module where options are simply passed.

    +

    koa uses the cookies module where options are simply passed.

    ctx.cookies.set(name, value, [options])

    Set cookie name to value with options:

      @@ -235,37 +244,29 @@

      ctx.cookies.set(name, value, [optio
    • httpOnly server-accessible cookie, true by default
    • overwrite 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.
    -

    Koa uses the cookies module where options are simply passed.

    +

    koa uses the cookies module where options are simply passed.

    ctx.throw([status], [msg], [properties])

    Helper method to throw an error with a .status property defaulting to 500 that will allow Koa to respond appropriately. The following combinations are allowed:

    -
    ctx.throw(400);
    -ctx.throw(400, 'name required');
    -ctx.throw(400, 'name required', { user: user });
    -
    -

    For example ctx.throw(400, 'name required') is equivalent to:

    -
    const err = new Error('name required');
    -err.status = 400;
    -err.expose = true;
    -throw err;
    -
    -

    Note that these are user-level errors and are flagged with +

    ctx.throw(400);
    +ctx.throw(400, 'name required');
    +ctx.throw(400, 'name required', { user: user });

    For example ctx.throw(400, 'name required') is equivalent to:

    +
    const err = new Error('name required');
    +err.status = 400;
    +err.expose = true;
    +throw err;

    Note that these are user-level errors and are flagged with err.expose meaning the messages are appropriate for client responses, which is typically not the case for error messages since you do not want to leak failure details.

    You may optionally pass a properties object which is merged into the error as-is, useful for decorating machine-friendly errors which are reported to the requester upstream.

    -
    ctx.throw(401, 'access_denied', { user: user });
    -
    -

    Koa uses http-errors to create errors.

    +
    ctx.throw(401, 'access_denied', { user: user });

    koa uses http-errors to create errors.

    ctx.assert(value, [status], [msg], [properties])

    Helper method to throw an error similar to .throw() when !value. Similar to node's assert() method.

    -
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');
    -
    -

    Koa uses http-assert for assertions.

    +
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    koa uses http-assert for assertions.

    ctx.respond

    To bypass Koa's built-in response handling, you may explicitly set ctx.respond = false;. Use this if you want to write to the raw res object instead of letting Koa handle the response for you.

    Note that using this is not supported by Koa. This may break intended functionality of Koa middleware and Koa itself. Using this property is considered a hack and is only a convenience to those wishing to use traditional fn(req, res) functions and middleware within Koa.

    @@ -354,15 +355,11 @@

    request.originalUrl

    Get request original URL.

    request.origin

    Get origin of URL, include protocol and host.

    -
    ctx.request.origin
    -// => http://example.com
    -
    -

    request.href

    +
    ctx.request.origin
    +// => http://example.com

    request.href

    Get full request URL, include protocol, host and url.

    -
    ctx.request.href;
    -// => http://example.com/foo/bar?q=1
    -
    -

    request.path

    +
    ctx.request.href
    +// => http://example.com/foo/bar?q=1

    request.path

    Get request pathname.

    request.path=

    Set request pathname and retain query-string when present.

    @@ -387,47 +384,37 @@

    request.URL

    Get WHATWG parsed URL object.

    request.type

    Get request Content-Type void of parameters such as "charset".

    -
    const ct = ctx.request.type;
    -// => "image/png"
    -
    -

    request.charset

    +
    const ct = ctx.request.type
    +// => "image/png"

    request.charset

    Get request charset when present, or undefined:

    -
    ctx.request.charset;
    -// => "utf-8"
    -
    -

    request.query

    +
    ctx.request.charset
    +// => "utf-8"

    request.query

    Get parsed query-string, returning an empty object when no query-string is present. Note that this getter does not support nested parsing.

    For example "color=blue&size=small":

    -
    {
    -  color: 'blue',
    -  size: 'small'
    -}
    -
    -

    request.query=

    +
    {
    +  color: 'blue',
    +  size: 'small'
    +}

    request.query=

    Set query-string to the given object. Note that this setter does not support nested objects.

    -
    ctx.query = { next: '/login' };
    -
    -

    request.fresh

    +
    ctx.query = { next: '/login' }

    request.fresh

    Check if a request cache is "fresh", aka the contents have not changed. This method is for cache negotiation between If-None-Match / ETag, and If-Modified-Since and Last-Modified. It should be referenced after setting one or more of these response headers.

    -
    // freshness check requires status 20x or 304
    -ctx.status = 200;
    -ctx.set('ETag', '123');
    +
    // freshness check requires status 20x or 304
    +ctx.status = 200;
    +ctx.set('ETag', '123');
     
    -// cache is ok
    -if (ctx.fresh) {
    -  ctx.status = 304;
    -  return;
    -}
    +// cache is ok
    +if (ctx.fresh) {
    +  ctx.status = 304;
    +  return;
    +}
     
    -// cache is stale
    -// fetch new data
    -ctx.body = await db.find('something');
    -
    -

    request.stale

    +// cache is stale +// fetch new data +ctx.body = yield db.find('something');

    request.stale

    Inverse of request.fresh.

    request.protocol

    Return request protocol, "https" or "http". Supports X-Forwarded-Proto @@ -456,27 +443,23 @@

    request.is(types...)

    If there is no request body, null is returned. If there is no content type, or the match fails false is returned. Otherwise, it returns the matching content-type.

    -
    // With Content-Type: text/html; charset=utf-8
    -ctx.is('html'); // => 'html'
    -ctx.is('text/html'); // => 'text/html'
    -ctx.is('text/*', 'text/html'); // => 'text/html'
    +
    // With Content-Type: text/html; charset=utf-8
    +ctx.is('html'); // => 'html'
    +ctx.is('text/html'); // => 'text/html'
    +ctx.is('text/*', 'text/html'); // => 'text/html'
     
    -// When Content-Type is application/json
    -ctx.is('json', 'urlencoded'); // => 'json'
    -ctx.is('application/json'); // => 'application/json'
    -ctx.is('html', 'application/*'); // => 'application/json'
    +// When Content-Type is application/json
    +ctx.is('json', 'urlencoded'); // => 'json'
    +ctx.is('application/json'); // => 'application/json'
    +ctx.is('html', 'application/*'); // => 'application/json'
     
    -ctx.is('html'); // => false
    -
    -

    For example if you want to ensure that +ctx.is('html'); // => false

    For example if you want to ensure that only images are sent to a given route:

    -
    if (ctx.is('image/*')) {
    -  // process
    -} else {
    -  ctx.throw(415, 'images only!');
    -}
    -
    -

    Content Negotiation

    +
    if (ctx.is('image/*')) {
    +  // process
    +} else {
    +  ctx.throw(415, 'images only!');
    +}

    Content Negotiation

    Koa's request object includes helpful content negotiation utilities powered by accepts and negotiator. These utilities are:

    • request.accepts(types)
    • @@ -491,94 +474,78 @@

      request.accepts(types)

      Check if the given type(s) is acceptable, returning the best match when true, otherwise false. The type value may be one or more mime type string such as "application/json", the extension name such as "json", or an array ["json", "html", "text/plain"].

      -
      // Accept: text/html
      -ctx.accepts('html');
      -// => "html"
      +
      // Accept: text/html
      +ctx.accepts('html');
      +// => "html"
       
      -// Accept: text/*, application/json
      -ctx.accepts('html');
      -// => "html"
      -ctx.accepts('text/html');
      -// => "text/html"
      -ctx.accepts('json', 'text');
      -// => "json"
      -ctx.accepts('application/json');
      -// => "application/json"
      +// Accept: text/*, application/json
      +ctx.accepts('html');
      +// => "html"
      +ctx.accepts('text/html');
      +// => "text/html"
      +ctx.accepts('json', 'text');
      +// => "json"
      +ctx.accepts('application/json');
      +// => "application/json"
       
      -// Accept: text/*, application/json
      -ctx.accepts('image/png');
      -ctx.accepts('png');
      -// => false
      +// Accept: text/*, application/json
      +ctx.accepts('image/png');
      +ctx.accepts('png');
      +// => false
       
      -// Accept: text/*;q=.5, application/json
      -ctx.accepts(['html', 'json']);
      -ctx.accepts('html', 'json');
      -// => "json"
      +// Accept: text/*;q=.5, application/json
      +ctx.accepts(['html', 'json']);
      +ctx.accepts('html', 'json');
      +// => "json"
       
      -// No Accept header
      -ctx.accepts('html', 'json');
      -// => "html"
      -ctx.accepts('json', 'html');
      -// => "json"
      -
      -

      You may call ctx.accepts() as many times as you like, +// No Accept header +ctx.accepts('html', 'json'); +// => "html" +ctx.accepts('json', 'html'); +// => "json"

      You may call ctx.accepts() as many times as you like, or use a switch:

      -
      switch (ctx.accepts('json', 'html', 'text')) {
      -  case 'json': break;
      -  case 'html': break;
      -  case 'text': break;
      -  default: ctx.throw(406, 'json, html, or text only');
      -}
      -
      -

      request.acceptsEncodings(encodings)

      +
      switch (ctx.accepts('json', 'html', 'text')) {
      +  case 'json': break;
      +  case 'html': break;
      +  case 'text': break;
      +  default: ctx.throw(406, 'json, html, or text only');
      +}

      request.acceptsEncodings(encodings)

      Check if encodings are acceptable, returning the best match when true, otherwise false. Note that you should include identity as one of the encodings!

      -
      // Accept-Encoding: gzip
      -ctx.acceptsEncodings('gzip', 'deflate', 'identity');
      -// => "gzip"
      +
      // Accept-Encoding: gzip
      +ctx.acceptsEncodings('gzip', 'deflate', 'identity');
      +// => "gzip"
       
      -ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
      -// => "gzip"
      -
      -

      When no arguments are given all accepted encodings +ctx.acceptsEncodings(['gzip', 'deflate', 'identity']); +// => "gzip"

      When no arguments are given all accepted encodings are returned as an array:

      -
      // Accept-Encoding: gzip, deflate
      -ctx.acceptsEncodings();
      -// => ["gzip", "deflate", "identity"]
      -
      -

      Note that the identity encoding (which means no encoding) could be unacceptable if the client explicitly sends identity;q=0. Although this is an edge case, you should still handle the case where this method returns false.

      +
      // Accept-Encoding: gzip, deflate
      +ctx.acceptsEncodings();
      +// => ["gzip", "deflate", "identity"]

      Note that the identity encoding (which means no encoding) could be unacceptable if the client explicitly sends identity;q=0. Although this is an edge case, you should still handle the case where this method returns false.

      request.acceptsCharsets(charsets)

      Check if charsets are acceptable, returning the best match when true, otherwise false.

      -
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      -ctx.acceptsCharsets('utf-8', 'utf-7');
      -// => "utf-8"
      +
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      +ctx.acceptsCharsets('utf-8', 'utf-7');
      +// => "utf-8"
       
      -ctx.acceptsCharsets(['utf-7', 'utf-8']);
      -// => "utf-8"
      -
      -

      When no arguments are given all accepted charsets +ctx.acceptsCharsets(['utf-7', 'utf-8']); +// => "utf-8"

      When no arguments are given all accepted charsets are returned as an array:

      -
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      -ctx.acceptsCharsets();
      -// => ["utf-8", "utf-7", "iso-8859-1"]
      -
      -

      request.acceptsLanguages(langs)

      +
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      +ctx.acceptsCharsets();
      +// => ["utf-8", "utf-7", "iso-8859-1"]

      request.acceptsLanguages(langs)

      Check if langs are acceptable, returning the best match when true, otherwise false.

      -
      // Accept-Language: en;q=0.8, es, pt
      -ctx.acceptsLanguages('es', 'en');
      -// => "es"
      +
      // Accept-Language: en;q=0.8, es, pt
      +ctx.acceptsLanguages('es', 'en');
      +// => "es"
       
      -ctx.acceptsLanguages(['en', 'es']);
      -// => "es"
      -
      -

      When no arguments are given all accepted languages +ctx.acceptsLanguages(['en', 'es']); +// => "es"

      When no arguments are given all accepted languages are returned as an array:

      -
      // Accept-Language: en;q=0.8, es, pt
      -ctx.acceptsLanguages();
      -// => ["es", "pt", "en"]
      -
      -

      request.idempotent

      +
      // Accept-Language: en;q=0.8, es, pt
      +ctx.acceptsLanguages();
      +// => ["es", "pt", "en"]

      request.idempotent

      Check if the request is idempotent.

      request.socket

      Return the request socket.

      @@ -639,7 +606,6 @@

      response.status=

    • 415 "unsupported media type"
    • 416 "range not satisfiable"
    • 417 "expectation failed"
    • -
    • 418 "I'm a teapot"
    • 422 "unprocessable entity"
    • 423 "locked"
    • 424 "failed dependency"
    • @@ -698,48 +664,34 @@

      Stream

      For example, you may not want this when setting the body as an HTTP stream in a proxy as it would destroy the underlying connection.

      See: https://github.com/koajs/koa/pull/612 for more information.

      Here's an example of stream error handling without automatically destroying the stream:

      -
      const PassThrough = require('stream').PassThrough;
      +
      const PassThrough = require('stream').PassThrough;
       
      -app.use(async ctx => {
      -  ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough());
      -});
      -
      -

      Object

      +app.use(function * (next) { + ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough()); +});

      Object

      The Content-Type is defaulted to application/json. This includes plain objects { foo: 'bar' } and arrays ['foo', 'bar'].

      response.get(field)

      Get a response header field value with case-insensitive field.

      -
      const etag = ctx.response.get('ETag');
      -
      -

      response.set(field, value)

      +
      const etag = ctx.get('ETag');

      response.set(field, value)

      Set response header field to value:

      -
      ctx.set('Cache-Control', 'no-cache');
      -
      -

      response.append(field, value)

      +
      ctx.set('Cache-Control', 'no-cache');

      response.append(field, value)

      Append additional header field with value val.

      -
      ctx.append('Link', '<http://127.0.0.1/>');
      -
      -

      response.set(fields)

      +
      ctx.append('Link', '<http://127.0.0.1/>');

      response.set(fields)

      Set several response header fields with an object:

      -
      ctx.set({
      -  'Etag': '1234',
      -  'Last-Modified': date
      -});
      -
      -

      response.remove(field)

      +
      ctx.set({
      +  'Etag': '1234',
      +  'Last-Modified': date
      +});

      response.remove(field)

      Remove header field.

      response.type

      Get response Content-Type void of parameters such as "charset".

      -
      const ct = ctx.type;
      -// => "image/png"
      -
      -

      response.type=

      +
      const ct = ctx.type;
      +// => "image/png"

      response.type=

      Set response Content-Type via mime string or file extension.

      -
      ctx.type = 'text/plain; charset=utf-8';
      -ctx.type = 'image/png';
      -ctx.type = '.png';
      -ctx.type = 'png';
      -
      -

      Note: when appropriate a charset is selected for you, for +

      ctx.type = 'text/plain; charset=utf-8';
      +ctx.type = 'image/png';
      +ctx.type = '.png';
      +ctx.type = 'png';

      Note: when appropriate a charset is selected for you, for example response.type = 'html' will default to "utf-8". If you need to overwrite charset, use ctx.set('Content-Type', 'text/html') to set response header field to value directly.

      response.is(types...)

      @@ -749,37 +701,31 @@

      response.is(types...)

      manipulate responses.

      For example, this is a middleware that minifies all HTML responses except for streams.

      -
      const minify = require('html-minifier');
      +
      const minify = require('html-minifier');
       
      -app.use(async (ctx, next) => {
      -  await next();
      +app.use(function * minifyHTML(next) {
      +  yield next;
       
      -  if (!ctx.response.is('html')) return;
      +  if (!ctx.response.is('html')) return;
       
      -  let body = ctx.body;
      -  if (!body || body.pipe) return;
      +  let body = ctx.body;
      +  if (!body || body.pipe) return;
       
      -  if (Buffer.isBuffer(body)) body = body.toString();
      -  ctx.body = minify(body);
      -});
      -
      -

      response.redirect(url, [alt])

      + if (Buffer.isBuffer(body)) body = body.toString(); + ctx.body = minify(body); +});

      response.redirect(url, [alt])

      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.

      -
      ctx.redirect('back');
      -ctx.redirect('back', '/index.html');
      -ctx.redirect('/login');
      -ctx.redirect('http://google.com');
      -
      -

      To alter the default status of 302, simply assign the status +

      ctx.redirect('back');
      +ctx.redirect('back', '/index.html');
      +ctx.redirect('/login');
      +ctx.redirect('http://google.com');

      To alter the default status of 302, simply assign the status before or after this call. To alter the body, assign it after this call:

      -
      ctx.status = 301;
      -ctx.redirect('/cart');
      -ctx.body = 'Redirecting to shopping cart';
      -
      -

      response.attachment([filename])

      +
      ctx.status = 301;
      +ctx.redirect('/cart');
      +ctx.body = 'Redirecting to shopping cart';

      response.attachment([filename])

      Set Content-Disposition to "attachment" to signal the client to prompt for download. Optionally specify the filename of the download.

      @@ -791,18 +737,14 @@

      response.lastModified

      response.lastModified=

      Set the Last-Modified header as an appropriate UTC string. You can either set it as a Date or date string.

      -
      ctx.response.lastModified = new Date();
      -
      -

      response.etag=

      +
      ctx.response.lastModified = new Date();

      response.etag=

      Set the ETag of a response including the wrapped "s. Note that there is no corresponding response.etag getter.

      -
      ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');
      -
      -

      response.vary(field)

      +
      ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');

      response.vary(field)

      Vary on field.

      response.flushHeaders()

      Flush any set headers, and begin the body.

    Jobs

    Looking for work with an amazing tech company? Check out these positions.

    Links

    Community links to discover third-party middleware for Koa, full runnable examples, thorough guides and more! If you have questions join us in IRC! -

    \ No newline at end of file +

    \ No newline at end of file diff --git a/options.js b/options.js new file mode 100644 index 0000000..7899594 --- /dev/null +++ b/options.js @@ -0,0 +1,56 @@ +{ + filters: + { + markdown: function (text) + { + var marked = require("marked"); + var renderer = new marked.Renderer(); + var prism = require('prismjs'); + + renderer.code = function(code, lang, escaped) { + + if (!global.exampleCount) + global.exampleCount = 0; + + var out = prism.highlight(code, prism.languages.javascript) || code; + var escaped = out !== code ? out : escapeCode(out, true); + var id = "example-" + (global.exampleCount++); + var script = lang === "runkit-endpoint" ? + "" : ""; + + return "
    " + escaped + "
    " + script; + }; + + return marked(text, { renderer: renderer }); + + function endpoint(id, count) { + if (!window.RunKit) + if (count || 0 < 20) + return setTimeout(endpoint, 500, id, count || 0 + 1); + else + return; + + var parent = document.getElementById(id); + var source = parent.textContent; + + parent.innerHTML = ""; + + RunKit.createNotebook({ + element: parent, + source: source, + mode: "endpoint", + onLoad: function () { alert() } + }); + } + + function escapeCode(code) { + return code + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index 95902a5..a909a6f 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,10 @@ "version": "0.0.1", "description": "the koa website", "dependencies": { - "jade": "~0.35.0", "marked": "~0.3.17", + "prismjs": "^1.6.0", + "pug": "^2.0.0-rc.2", + "pug-cli": "^1.0.0-alpha6", "stdin": "0.0.1" } } diff --git a/views/index.jade b/views/index.jade index b0d6c77..3628604 100644 --- a/views/index.jade +++ b/views/index.jade @@ -13,6 +13,7 @@ html window.analytics.page(); script(src='stats.js') script(src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/highlight.min.js') + script(src="https://embed.runkit.com", async, defer) body section#top #menu @@ -42,19 +43,19 @@ html section .content - include ../public/index.md + include:markdown ../public/index.md section .content - include ../public/context.md + include:markdown ../public/context.md section .content - include ../public/request.md + include:markdown ../public/request.md section .content - include ../public/response.md + include:markdown ../public/response.md section .content From f334bb22d0334ab05c3278fe8e67df24bd425c07 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Tue, 4 Jul 2017 22:09:50 -0700 Subject: [PATCH 2/8] Fix remove alert. Reviewed by @tolmasky. --- index.html | 6 ++---- options.js | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/index.html b/index.html index e1e3ca9..8964b4b 100644 --- a/index.html +++ b/index.html @@ -55,8 +55,7 @@

    Application

    RunKit.createNotebook({ element: parent, source: source, - mode: "endpoint", - onLoad: function () { alert() } + mode: "endpoint" }); })("example-3")

    This example is actually live, and you can visit the displayed URL to see it in action. If you edit it above, you'll see the changes take place instantly.

    Cascading

    @@ -113,8 +112,7 @@

    Cascading

    RunKit.createNotebook({ element: parent, source: source, - mode: "endpoint", - onLoad: function () { alert() } + mode: "endpoint" }); })("example-4")

    This example is also live, by going to it in another tab you'll see the logs above.

    Settings

    diff --git a/options.js b/options.js index 7899594..e290f24 100644 --- a/options.js +++ b/options.js @@ -38,8 +38,7 @@ RunKit.createNotebook({ element: parent, source: source, - mode: "endpoint", - onLoad: function () { alert() } + mode: "endpoint" }); } From a021939539df9f6c6b067b71099e4d0c8934214f Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Fri, 21 Jul 2017 09:15:54 -0700 Subject: [PATCH 3/8] Change file. Reviewed by @tolmasky. --- Makefile | 2 +- options.js => pug-options.js | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename options.js => pug-options.js (100%) diff --git a/Makefile b/Makefile index 3344ad7..7601143 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ DOCS = public/index.md \ public/guide.md index.html: views/index.jade $(DOCS) - @./node_modules/.bin/pug --obj options.js $< -o . + @./node_modules/.bin/pug --obj pug-options.js $< -o . public/index.md: curl -s $(RAW)/koajs/koa/master/docs/api/index.md \ diff --git a/options.js b/pug-options.js similarity index 100% rename from options.js rename to pug-options.js From 35d540289be5bb0095229b8bc3aa358f1629dab7 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 22 Jul 2017 12:47:53 -0700 Subject: [PATCH 4/8] Fix conflict. Reviewed by @tolmasky. --- index.html | 427 ++++++++++++++++++++++++----------------------- pug-options.js | 28 +++- views/index.jade | 3 +- 3 files changed, 239 insertions(+), 219 deletions(-) diff --git a/index.html b/index.html index 8964b4b..f8019d6 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ -Koa - next generation web framework for node.js
    next generation web framework for node.js

    Introduction

    Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation @@ -9,19 +9,19 @@

    Installation

    Koa requires node v7.6.0 or higher for ES2015 and async function support.

    You can quickly install a supported version of node with your favorite version manager:

    -
    $ nvm install 7
    +
    $ nvm install 7
     $ npm i koa
    -$ node my-koa-app.js

    Async Functions with Babel

    +$ node my-koa-app.js

    Async Functions with Babel

    To use async functions in Koa in versions of node < 7.6, we recommend using babel's require hook.

    -
    require('babel-core/register');
    -// require the rest of the app that needs to be transpiled after the hook
    -const app = require('./app');

    To parse and transpile async functions, +

    require('babel-core/register');
    +// require the rest of the app that needs to be transpiled after the hook
    +const app = require('./app');

    To parse and transpile async functions, you should at a minimum have the transform-async-to-generator or transform-async-to-module-method plugins. For example, in your .babelrc file, you should have:

    -
    {
    -  "plugins": ["transform-async-to-generator"]
    -}

    You can also use the env preset with a target option "node": "current" instead.

    +
    {
    +  "plugins": ["transform-async-to-generator"]
    +}

    You can also use the env preset with a target option "node": "current" instead.

    Application

    A Koa application is an object containing an array of middleware functions which are composed and executed in a stack-like manner upon request. Koa is similar to many @@ -33,16 +33,18 @@

    Application

    among others. Despite supplying a reasonably large number of helpful methods Koa maintains a small footprint, as no middleware are bundled.

    The obligatory hello world application:

    -
    const Koa = require('koa');
    -const app = new Koa();
    +
    +
    const Koa = require('koa');
    +const app = new Koa();
     
    -app.use(ctx => {
    -  ctx.body = 'Hello World';
    -});
    +app.use(async ctx => {
    +  ctx.body = 'Hello World';
    +});
     
    -app.listen(3000);

    This example is actually live, and you can visit the displayed URL to see it in action. If you edit it above, you'll see the changes take place instantly.

    -

    Cascading

    + })("example-3")

    Cascading

    Koa middleware cascade in a more traditional way as you may be used to with similar tools - this was previously difficult to make user friendly with node's use of callbacks. However with async functions we can achieve "true" middleware. Contrasting Connect's implementation which @@ -70,36 +72,38 @@

    Cascading

    the function suspends and passes control to the next middleware defined. After there are no more middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.

    -
    const Koa = require('koa');
    -const app = new Koa();
    +
    +
    const Koa = require('koa');
    +const app = new Koa();
     
    -// x-response-time
    +// x-response-time
     
    -app.use(async function (ctx, next) {
    -  const start = Date.now();
    -  await next();
    -  const ms = Date.now() - start;
    -  ctx.set('X-Response-Time', `${ms}ms`);
    -});
    +app.use(async (ctx, next) => {
    +  const start = Date.now();
    +  await next();
    +  const ms = Date.now() - start;
    +  ctx.set('X-Response-Time', `${ms}ms`);
    +});
     
    -// logger
    +// logger
     
    -app.use(async function (ctx, next) {
    -  const start = Date.now();
    -  await next();
    -  const ms = Date.now() - start;
    -  console.log(`${ctx.method} ${ctx.url} - ${ms}`);
    -});
    +app.use(async (ctx, next) => {
    +  const start = Date.now();
    +  await next();
    +  const ms = Date.now() - start;
    +  console.log(`${ctx.method} ${ctx.url} - ${ms}`);
    +});
     
    -// response
    +// response
     
    -app.use(ctx => {
    -  ctx.body = 'Hello World';
    -});
    +app.use(async ctx => {
    +  ctx.body = 'Hello World';
    +});
     
    -app.listen(3000);

    This example is also live, by going to it in another tab you'll see the logs above.

    -

    Settings

    + })("example-4")

    Settings

    Application settings are properties on the app instance, currently the following are supported:

      @@ -129,20 +133,20 @@

      app.listen(...)

      applications with a single HTTP server.

      Create and return an HTTP server, passing the given arguments to Server#listen(). These arguments are documented on nodejs.org. The following is a useless Koa application bound to port 3000:

      -
      const Koa = require('koa');
      -const app = new Koa();
      -app.listen(3000);

      The app.listen(...) method is simply sugar for the following:

      -
      const http = require('http');
      -const Koa = require('koa');
      -const app = new Koa();
      -http.createServer(app.callback()).listen(3000);

      This means you can spin up the same application as both HTTP and HTTPS +

      const Koa = require('koa');
      +const app = new Koa();
      +app.listen(3000);

      The app.listen(...) method is simply sugar for the following:

      +
      const http = require('http');
      +const Koa = require('koa');
      +const app = new Koa();
      +http.createServer(app.callback()).listen(3000);

      This means you can spin up the same application as both HTTP and HTTPS or on multiple addresses:

      -
      const http = require('http');
      -const https = require('https');
      -const Koa = require('koa');
      -const app = new Koa();
      -http.createServer(app.callback()).listen(3000);
      -https.createServer(app.callback()).listen(3001);

      app.callback()

      +
      const http = require('http');
      +const https = require('https');
      +const Koa = require('koa');
      +const app = new Koa();
      +http.createServer(app.callback()).listen(3000);
      +https.createServer(app.callback()).listen(3001);

      app.callback()

      Return a callback function suitable for the http.createServer() method to handle a request. You may also use this callback function to mount your koa app in a @@ -155,21 +159,21 @@

      app.keys=

      These are passed to KeyGrip, however you may also pass your own KeyGrip instance. For example the following are acceptable:

      -
      app.keys = ['im a newer secret', 'i like turtle'];
      -app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

      These keys may be rotated and are used when signing cookies +

      app.keys = ['im a newer secret', 'i like turtle'];
      +app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

      These keys may be rotated and are used when signing cookies with the { signed: true } option:

      -
      ctx.cookies.set('name', 'tobi', { signed: true });

      app.context

      +
      ctx.cookies.set('name', 'tobi', { signed: true });

      app.context

      app.context is the prototype from which ctx is created from. You may add additional properties to ctx by editing app.context. This is useful for adding properties or methods to ctx to be used across your entire app, which may be more performant (no middleware) and/or easier (fewer require()s) at the expense of relying more on ctx, which could be considered an anti-pattern.

      For example, to add a reference to your database from ctx:

      -
      app.context.db = db();
      +
      app.context.db = db();
       
      -app.use(async (ctx) => {
      -  console.log(ctx.db);
      -});

      Note:

      +app.use(async ctx => { + console.log(ctx.db); +});

      Note:

      • Many properties on ctx are defined using getters, setters, and Object.defineProperty(). You can only edit these properties (not recommended) by using Object.defineProperty() on app.context. See https://github.com/koajs/koa/issues/652.
      • Mounted apps currently use its parent's ctx and settings. Thus, mounted apps are really just groups of middleware.
      • @@ -178,12 +182,12 @@

        Error Handling

        By default outputs all errors to stderr unless app.silent is true. The default error handler also won't outputs errors when err.status is 404 or err.expose is true. To perform custom error-handling logic such as centralized logging you can add an "error" event listener:

        -
        app.on('error', err =>
        -  log.error('server error', err)
        -);

        If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

        -
        app.on('error', (err, ctx) =>
        -  log.error('server error', err, ctx)
        -);

        When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond +

        app.on('error', err => {
        +  log.error('server error', err)
        +});

        If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

        +
        app.on('error', (err, ctx) => {
        +  log.error('server error', err, ctx)
        +});

        When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond appropriately with a 500 "Internal Server Error". In either case an app-level "error" is emitted for logging purposes.

    Context

    @@ -196,11 +200,11 @@

    Error Handling

    A Context is created per request, and is referenced in middleware as the receiver, or the ctx identifier, as shown in the following snippet:

    -
    app.use(async (ctx, next) => {
    -  ctx; // is the Context
    -  ctx.request; // is a koa Request
    -  ctx.response; // is a koa Response
    -});

    Many of the context's accessors and methods simply delegate to their ctx.request or ctx.response +

    app.use(async ctx => {
    +  ctx; // is the Context
    +  ctx.request; // is a koa Request
    +  ctx.response; // is a koa Response
    +});

    Many of the context's accessors and methods simply delegate to their ctx.request or ctx.response equivalents for convenience, and are otherwise identical. For example ctx.type and ctx.length delegate to the response object, and ctx.path and ctx.method delegate to the request.

    API

    @@ -222,7 +226,7 @@

    ctx.response

    A koa Response object.

    ctx.state

    The recommended namespace for passing information through middleware and to your frontend views.

    -
    ctx.state.user = await User.find(id);

    ctx.app

    +
    ctx.state.user = await User.find(id);

    ctx.app

    Application instance reference.

    ctx.cookies.get(name, [options])

    Get cookie name with options:

    @@ -247,24 +251,24 @@

    ctx.throw([status], [msg], [properties

    Helper method to throw an error with a .status property defaulting to 500 that will allow Koa to respond appropriately. The following combinations are allowed:

    -
    ctx.throw(400);
    -ctx.throw(400, 'name required');
    -ctx.throw(400, 'name required', { user: user });

    For example ctx.throw(400, 'name required') is equivalent to:

    -
    const err = new Error('name required');
    -err.status = 400;
    -err.expose = true;
    -throw err;

    Note that these are user-level errors and are flagged with +

    ctx.throw(400);
    +ctx.throw(400, 'name required');
    +ctx.throw(400, 'name required', { user: user });

    For example ctx.throw(400, 'name required') is equivalent to:

    +
    const err = new Error('name required');
    +err.status = 400;
    +err.expose = true;
    +throw err;

    Note that these are user-level errors and are flagged with err.expose meaning the messages are appropriate for client responses, which is typically not the case for error messages since you do not want to leak failure details.

    You may optionally pass a properties object which is merged into the error as-is, useful for decorating machine-friendly errors which are reported to the requester upstream.

    -
    ctx.throw(401, 'access_denied', { user: user });

    koa uses http-errors to create errors.

    +
    ctx.throw(401, 'access_denied', { user: user });

    koa uses http-errors to create errors.

    ctx.assert(value, [status], [msg], [properties])

    Helper method to throw an error similar to .throw() when !value. Similar to node's assert() method.

    -
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    koa uses http-assert for assertions.

    +
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    koa uses http-assert for assertions.

    ctx.respond

    To bypass Koa's built-in response handling, you may explicitly set ctx.respond = false;. Use this if you want to write to the raw res object instead of letting Koa handle the response for you.

    Note that using this is not supported by Koa. This may break intended functionality of Koa middleware and Koa itself. Using this property is considered a hack and is only a convenience to those wishing to use traditional fn(req, res) functions and middleware within Koa.

    @@ -353,11 +357,11 @@

    request.originalUrl

    Get request original URL.

    request.origin

    Get origin of URL, include protocol and host.

    -
    ctx.request.origin
    -// => http://example.com

    request.href

    +
    ctx.request.origin
    +// => http://example.com

    request.href

    Get full request URL, include protocol, host and url.

    -
    ctx.request.href
    -// => http://example.com/foo/bar?q=1

    request.path

    +
    ctx.request.href;
    +// => http://example.com/foo/bar?q=1

    request.path

    Get request pathname.

    request.path=

    Set request pathname and retain query-string when present.

    @@ -382,37 +386,37 @@

    request.URL

    Get WHATWG parsed URL object.

    request.type

    Get request Content-Type void of parameters such as "charset".

    -
    const ct = ctx.request.type
    -// => "image/png"

    request.charset

    +
    const ct = ctx.request.type;
    +// => "image/png"

    request.charset

    Get request charset when present, or undefined:

    -
    ctx.request.charset
    -// => "utf-8"

    request.query

    +
    ctx.request.charset;
    +// => "utf-8"

    request.query

    Get parsed query-string, returning an empty object when no query-string is present. Note that this getter does not support nested parsing.

    For example "color=blue&size=small":

    -
    {
    -  color: 'blue',
    -  size: 'small'
    -}

    request.query=

    +
    {
    +  color: 'blue',
    +  size: 'small'
    +}

    request.query=

    Set query-string to the given object. Note that this setter does not support nested objects.

    -
    ctx.query = { next: '/login' }

    request.fresh

    +
    ctx.query = { next: '/login' };

    request.fresh

    Check if a request cache is "fresh", aka the contents have not changed. This method is for cache negotiation between If-None-Match / ETag, and If-Modified-Since and Last-Modified. It should be referenced after setting one or more of these response headers.

    -
    // freshness check requires status 20x or 304
    -ctx.status = 200;
    -ctx.set('ETag', '123');
    +
    // freshness check requires status 20x or 304
    +ctx.status = 200;
    +ctx.set('ETag', '123');
     
    -// cache is ok
    -if (ctx.fresh) {
    -  ctx.status = 304;
    -  return;
    -}
    +// cache is ok
    +if (ctx.fresh) {
    +  ctx.status = 304;
    +  return;
    +}
     
    -// cache is stale
    -// fetch new data
    -ctx.body = yield db.find('something');

    request.stale

    +// cache is stale +// fetch new data +ctx.body = await db.find('something');

    request.stale

    Inverse of request.fresh.

    request.protocol

    Return request protocol, "https" or "http". Supports X-Forwarded-Proto @@ -441,23 +445,23 @@

    request.is(types...)

    If there is no request body, null is returned. If there is no content type, or the match fails false is returned. Otherwise, it returns the matching content-type.

    -
    // With Content-Type: text/html; charset=utf-8
    -ctx.is('html'); // => 'html'
    -ctx.is('text/html'); // => 'text/html'
    -ctx.is('text/*', 'text/html'); // => 'text/html'
    +
    // With Content-Type: text/html; charset=utf-8
    +ctx.is('html'); // => 'html'
    +ctx.is('text/html'); // => 'text/html'
    +ctx.is('text/*', 'text/html'); // => 'text/html'
     
    -// When Content-Type is application/json
    -ctx.is('json', 'urlencoded'); // => 'json'
    -ctx.is('application/json'); // => 'application/json'
    -ctx.is('html', 'application/*'); // => 'application/json'
    +// When Content-Type is application/json
    +ctx.is('json', 'urlencoded'); // => 'json'
    +ctx.is('application/json'); // => 'application/json'
    +ctx.is('html', 'application/*'); // => 'application/json'
     
    -ctx.is('html'); // => false

    For example if you want to ensure that +ctx.is('html'); // => false

    For example if you want to ensure that only images are sent to a given route:

    -
    if (ctx.is('image/*')) {
    -  // process
    -} else {
    -  ctx.throw(415, 'images only!');
    -}

    Content Negotiation

    +
    if (ctx.is('image/*')) {
    +  // process
    +} else {
    +  ctx.throw(415, 'images only!');
    +}

    Content Negotiation

    Koa's request object includes helpful content negotiation utilities powered by accepts and negotiator. These utilities are:

    • request.accepts(types)
    • @@ -472,78 +476,78 @@

      request.accepts(types)

      Check if the given type(s) is acceptable, returning the best match when true, otherwise false. The type value may be one or more mime type string such as "application/json", the extension name such as "json", or an array ["json", "html", "text/plain"].

      -
      // Accept: text/html
      -ctx.accepts('html');
      -// => "html"
      +
      // Accept: text/html
      +ctx.accepts('html');
      +// => "html"
       
      -// Accept: text/*, application/json
      -ctx.accepts('html');
      -// => "html"
      -ctx.accepts('text/html');
      -// => "text/html"
      -ctx.accepts('json', 'text');
      -// => "json"
      -ctx.accepts('application/json');
      -// => "application/json"
      +// Accept: text/*, application/json
      +ctx.accepts('html');
      +// => "html"
      +ctx.accepts('text/html');
      +// => "text/html"
      +ctx.accepts('json', 'text');
      +// => "json"
      +ctx.accepts('application/json');
      +// => "application/json"
       
      -// Accept: text/*, application/json
      -ctx.accepts('image/png');
      -ctx.accepts('png');
      -// => false
      +// Accept: text/*, application/json
      +ctx.accepts('image/png');
      +ctx.accepts('png');
      +// => false
       
      -// Accept: text/*;q=.5, application/json
      -ctx.accepts(['html', 'json']);
      -ctx.accepts('html', 'json');
      -// => "json"
      +// Accept: text/*;q=.5, application/json
      +ctx.accepts(['html', 'json']);
      +ctx.accepts('html', 'json');
      +// => "json"
       
      -// No Accept header
      -ctx.accepts('html', 'json');
      -// => "html"
      -ctx.accepts('json', 'html');
      -// => "json"

      You may call ctx.accepts() as many times as you like, +// No Accept header +ctx.accepts('html', 'json'); +// => "html" +ctx.accepts('json', 'html'); +// => "json"

      You may call ctx.accepts() as many times as you like, or use a switch:

      -
      switch (ctx.accepts('json', 'html', 'text')) {
      -  case 'json': break;
      -  case 'html': break;
      -  case 'text': break;
      -  default: ctx.throw(406, 'json, html, or text only');
      -}

      request.acceptsEncodings(encodings)

      +
      switch (ctx.accepts('json', 'html', 'text')) {
      +  case 'json': break;
      +  case 'html': break;
      +  case 'text': break;
      +  default: ctx.throw(406, 'json, html, or text only');
      +}

      request.acceptsEncodings(encodings)

      Check if encodings are acceptable, returning the best match when true, otherwise false. Note that you should include identity as one of the encodings!

      -
      // Accept-Encoding: gzip
      -ctx.acceptsEncodings('gzip', 'deflate', 'identity');
      -// => "gzip"
      +
      // Accept-Encoding: gzip
      +ctx.acceptsEncodings('gzip', 'deflate', 'identity');
      +// => "gzip"
       
      -ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
      -// => "gzip"

      When no arguments are given all accepted encodings +ctx.acceptsEncodings(['gzip', 'deflate', 'identity']); +// => "gzip"

      When no arguments are given all accepted encodings are returned as an array:

      -
      // Accept-Encoding: gzip, deflate
      -ctx.acceptsEncodings();
      -// => ["gzip", "deflate", "identity"]

      Note that the identity encoding (which means no encoding) could be unacceptable if the client explicitly sends identity;q=0. Although this is an edge case, you should still handle the case where this method returns false.

      +
      // Accept-Encoding: gzip, deflate
      +ctx.acceptsEncodings();
      +// => ["gzip", "deflate", "identity"]

      Note that the identity encoding (which means no encoding) could be unacceptable if the client explicitly sends identity;q=0. Although this is an edge case, you should still handle the case where this method returns false.

      request.acceptsCharsets(charsets)

      Check if charsets are acceptable, returning the best match when true, otherwise false.

      -
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      -ctx.acceptsCharsets('utf-8', 'utf-7');
      -// => "utf-8"
      +
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      +ctx.acceptsCharsets('utf-8', 'utf-7');
      +// => "utf-8"
       
      -ctx.acceptsCharsets(['utf-7', 'utf-8']);
      -// => "utf-8"

      When no arguments are given all accepted charsets +ctx.acceptsCharsets(['utf-7', 'utf-8']); +// => "utf-8"

      When no arguments are given all accepted charsets are returned as an array:

      -
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      -ctx.acceptsCharsets();
      -// => ["utf-8", "utf-7", "iso-8859-1"]

      request.acceptsLanguages(langs)

      +
      // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
      +ctx.acceptsCharsets();
      +// => ["utf-8", "utf-7", "iso-8859-1"]

      request.acceptsLanguages(langs)

      Check if langs are acceptable, returning the best match when true, otherwise false.

      -
      // Accept-Language: en;q=0.8, es, pt
      -ctx.acceptsLanguages('es', 'en');
      -// => "es"
      +
      // Accept-Language: en;q=0.8, es, pt
      +ctx.acceptsLanguages('es', 'en');
      +// => "es"
       
      -ctx.acceptsLanguages(['en', 'es']);
      -// => "es"

      When no arguments are given all accepted languages +ctx.acceptsLanguages(['en', 'es']); +// => "es"

      When no arguments are given all accepted languages are returned as an array:

      -
      // Accept-Language: en;q=0.8, es, pt
      -ctx.acceptsLanguages();
      -// => ["es", "pt", "en"]

      request.idempotent

      +
      // Accept-Language: en;q=0.8, es, pt
      +ctx.acceptsLanguages();
      +// => ["es", "pt", "en"]

      request.idempotent

      Check if the request is idempotent.

      request.socket

      Return the request socket.

      @@ -604,6 +608,7 @@

      response.status=

    • 415 "unsupported media type"
    • 416 "range not satisfiable"
    • 417 "expectation failed"
    • +
    • 418 "I'm a teapot"
    • 422 "unprocessable entity"
    • 423 "locked"
    • 424 "failed dependency"
    • @@ -662,34 +667,34 @@

      Stream

      For example, you may not want this when setting the body as an HTTP stream in a proxy as it would destroy the underlying connection.

      See: https://github.com/koajs/koa/pull/612 for more information.

      Here's an example of stream error handling without automatically destroying the stream:

      -
      const PassThrough = require('stream').PassThrough;
      +
      const PassThrough = require('stream').PassThrough;
       
      -app.use(function * (next) {
      -  ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough());
      -});

      Object

      +app.use(async ctx => { + ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough()); +});

      Object

      The Content-Type is defaulted to application/json. This includes plain objects { foo: 'bar' } and arrays ['foo', 'bar'].

      response.get(field)

      Get a response header field value with case-insensitive field.

      -
      const etag = ctx.get('ETag');

      response.set(field, value)

      +
      const etag = ctx.response.get('ETag');

      response.set(field, value)

      Set response header field to value:

      -
      ctx.set('Cache-Control', 'no-cache');

      response.append(field, value)

      +
      ctx.set('Cache-Control', 'no-cache');

      response.append(field, value)

      Append additional header field with value val.

      -
      ctx.append('Link', '<http://127.0.0.1/>');

      response.set(fields)

      +
      ctx.append('Link', '<http://127.0.0.1/>');

      response.set(fields)

      Set several response header fields with an object:

      -
      ctx.set({
      -  'Etag': '1234',
      -  'Last-Modified': date
      -});

      response.remove(field)

      +
      ctx.set({
      +  'Etag': '1234',
      +  'Last-Modified': date
      +});

      response.remove(field)

      Remove header field.

      response.type

      Get response Content-Type void of parameters such as "charset".

      -
      const ct = ctx.type;
      -// => "image/png"

      response.type=

      +
      const ct = ctx.type;
      +// => "image/png"

      response.type=

      Set response Content-Type via mime string or file extension.

      -
      ctx.type = 'text/plain; charset=utf-8';
      -ctx.type = 'image/png';
      -ctx.type = '.png';
      -ctx.type = 'png';

      Note: when appropriate a charset is selected for you, for +

      ctx.type = 'text/plain; charset=utf-8';
      +ctx.type = 'image/png';
      +ctx.type = '.png';
      +ctx.type = 'png';

      Note: when appropriate a charset is selected for you, for example response.type = 'html' will default to "utf-8". If you need to overwrite charset, use ctx.set('Content-Type', 'text/html') to set response header field to value directly.

      response.is(types...)

      @@ -699,31 +704,31 @@

      response.is(types...)

      manipulate responses.

      For example, this is a middleware that minifies all HTML responses except for streams.

      -
      const minify = require('html-minifier');
      +
      const minify = require('html-minifier');
       
      -app.use(function * minifyHTML(next) {
      -  yield next;
      +app.use(async (ctx, next) => {
      +  await next();
       
      -  if (!ctx.response.is('html')) return;
      +  if (!ctx.response.is('html')) return;
       
      -  let body = ctx.body;
      -  if (!body || body.pipe) return;
      +  let body = ctx.body;
      +  if (!body || body.pipe) return;
       
      -  if (Buffer.isBuffer(body)) body = body.toString();
      -  ctx.body = minify(body);
      -});

      response.redirect(url, [alt])

      + if (Buffer.isBuffer(body)) body = body.toString(); + ctx.body = minify(body); +});

      response.redirect(url, [alt])

      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.

      -
      ctx.redirect('back');
      -ctx.redirect('back', '/index.html');
      -ctx.redirect('/login');
      -ctx.redirect('http://google.com');

      To alter the default status of 302, simply assign the status +

      ctx.redirect('back');
      +ctx.redirect('back', '/index.html');
      +ctx.redirect('/login');
      +ctx.redirect('http://google.com');

      To alter the default status of 302, simply assign the status before or after this call. To alter the body, assign it after this call:

      -
      ctx.status = 301;
      -ctx.redirect('/cart');
      -ctx.body = 'Redirecting to shopping cart';

      response.attachment([filename])

      +
      ctx.status = 301;
      +ctx.redirect('/cart');
      +ctx.body = 'Redirecting to shopping cart';

      response.attachment([filename])

      Set Content-Disposition to "attachment" to signal the client to prompt for download. Optionally specify the filename of the download.

      @@ -735,10 +740,10 @@

      response.lastModified

      response.lastModified=

      Set the Last-Modified header as an appropriate UTC string. You can either set it as a Date or date string.

      -
      ctx.response.lastModified = new Date();

      response.etag=

      +
      ctx.response.lastModified = new Date();

      response.etag=

      Set the ETag of a response including the wrapped "s. Note that there is no corresponding response.etag getter.

      -
      ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');

      response.vary(field)

      +
      ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');

      response.vary(field)

      Vary on field.

      response.flushHeaders()

      Flush any set headers, and begin the body.

      diff --git a/pug-options.js b/pug-options.js index e290f24..012ff2e 100644 --- a/pug-options.js +++ b/pug-options.js @@ -5,27 +5,42 @@ { var marked = require("marked"); var renderer = new marked.Renderer(); - var prism = require('prismjs'); + var highlight = require('highlight.js').highlight; + var runkitRegExp = /^(.|\n)*$/; + var runkitContext = { options: '{}', activated: false }; renderer.code = function(code, lang, escaped) { if (!global.exampleCount) global.exampleCount = 0; - var out = prism.highlight(code, prism.languages.javascript) || code; + var out = highlight(lang, code).value || code; var escaped = out !== code ? out : escapeCode(out, true); var id = "example-" + (global.exampleCount++); - var script = lang === "runkit-endpoint" ? - "" : ""; + + var script = runkitContext.activated ? "" : ""; + + runkitContext.activated = false; return "
      " + escaped + "
      " + script; }; + renderer.html = function(text) { + var result = runkitRegExp.exec(text); + + if (!result) return text; + + runkitContext.activated = true; + + return text; + }; + return marked(text, { renderer: renderer }); - + function endpoint(id, count) { + if (!window.RunKit) - if (count || 0 < 20) + if (typeof count === "undefined" || count < 20) return setTimeout(endpoint, 500, id, count || 0 + 1); else return; @@ -37,6 +52,7 @@ RunKit.createNotebook({ element: parent, + nodeVersion: "8.x.x", source: source, mode: "endpoint" }); diff --git a/views/index.jade b/views/index.jade index 3628604..10a682f 100644 --- a/views/index.jade +++ b/views/index.jade @@ -87,5 +87,4 @@ html li: a(href='https://github.com/koajs/koa/blob/master/docs/guide.md') Guide li: a(href='https://github.com/koajs/koa/blob/master/docs/faq.md') FAQ li: #koajs on freenode - script. - hljs.initHighlightingOnLoad(); + From 4e344c2b9b6799222c97b450119779cefad24fdd Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sat, 22 Jul 2017 12:52:41 -0700 Subject: [PATCH 5/8] Add highlight.js to package.json. Reveiwed by @tolmasky. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a909a6f..f5308d5 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "version": "0.0.1", "description": "the koa website", "dependencies": { + "highlight.js": "^9.12.0", "marked": "~0.3.17", - "prismjs": "^1.6.0", "pug": "^2.0.0-rc.2", "pug-cli": "^1.0.0-alpha6", "stdin": "0.0.1" From 1c7410867645bf3d1e9966f55959beb3c5d5bf25 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Sun, 30 Jul 2017 15:23:01 -0700 Subject: [PATCH 6/8] Update index.html. Fix code. Reviewed by @tolmasky. --- index.html | 96 +++++++++++++++++++++++++------------------------- pug-options.js | 2 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/index.html b/index.html index f8019d6..c6fc7f4 100644 --- a/index.html +++ b/index.html @@ -9,17 +9,17 @@

    Installation

    Koa requires node v7.6.0 or higher for ES2015 and async function support.

    You can quickly install a supported version of node with your favorite version manager:

    -
    $ nvm install 7
    +
    $ nvm install 7
     $ npm i koa
     $ node my-koa-app.js

    Async Functions with Babel

    To use async functions in Koa in versions of node < 7.6, we recommend using babel's require hook.

    -
    require('babel-core/register');
    +
    require('babel-core/register');
     // require the rest of the app that needs to be transpiled after the hook
     const app = require('./app');

    To parse and transpile async functions, you should at a minimum have the transform-async-to-generator or transform-async-to-module-method plugins. For example, in your .babelrc file, you should have:

    -
    {
    +
    {
       "plugins": ["transform-async-to-generator"]
     }

    You can also use the env preset with a target option "node": "current" instead.

    Application

    @@ -34,7 +34,7 @@

    Application

    no middleware are bundled.

    The obligatory hello world application:

    -
    const Koa = require('koa');
    +
    const Koa = require('koa');
     const app = new Koa();
     
     app.use(async ctx => {
    @@ -73,7 +73,7 @@ 

    Application

    middleware to execute downstream, the stack will unwind and each middleware is resumed to perform its upstream behaviour.

    -
    const Koa = require('koa');
    +
    const Koa = require('koa');
     const app = new Koa();
     
     // x-response-time
    @@ -133,15 +133,15 @@ 

    app.listen(...)

    applications with a single HTTP server.

    Create and return an HTTP server, passing the given arguments to Server#listen(). These arguments are documented on nodejs.org. The following is a useless Koa application bound to port 3000:

    -
    const Koa = require('koa');
    +
    const Koa = require('koa');
     const app = new Koa();
     app.listen(3000);

    The app.listen(...) method is simply sugar for the following:

    -
    const http = require('http');
    +
    const http = require('http');
     const Koa = require('koa');
     const app = new Koa();
     http.createServer(app.callback()).listen(3000);

    This means you can spin up the same application as both HTTP and HTTPS or on multiple addresses:

    -
    const http = require('http');
    +
    const http = require('http');
     const https = require('https');
     const Koa = require('koa');
     const app = new Koa();
    @@ -159,17 +159,17 @@ 

    app.keys=

    These are passed to KeyGrip, however you may also pass your own KeyGrip instance. For example the following are acceptable:

    -
    app.keys = ['im a newer secret', 'i like turtle'];
    +
    app.keys = ['im a newer secret', 'i like turtle'];
     app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

    These keys may be rotated and are used when signing cookies with the { signed: true } option:

    -
    ctx.cookies.set('name', 'tobi', { signed: true });

    app.context

    +
    ctx.cookies.set('name', 'tobi', { signed: true });

    app.context

    app.context is the prototype from which ctx is created from. You may add additional properties to ctx by editing app.context. This is useful for adding properties or methods to ctx to be used across your entire app, which may be more performant (no middleware) and/or easier (fewer require()s) at the expense of relying more on ctx, which could be considered an anti-pattern.

    For example, to add a reference to your database from ctx:

    -
    app.context.db = db();
    +
    app.context.db = db();
     
     app.use(async ctx => {
       console.log(ctx.db);
    @@ -182,10 +182,10 @@ 

    Error Handling

    By default outputs all errors to stderr unless app.silent is true. The default error handler also won't outputs errors when err.status is 404 or err.expose is true. To perform custom error-handling logic such as centralized logging you can add an "error" event listener:

    -
    app.on('error', err => {
    +
    app.on('error', err => {
       log.error('server error', err)
     });

    If an error is in the req/res cycle and it is not possible to respond to the client, the Context instance is also passed:

    -
    app.on('error', (err, ctx) => {
    +
    app.on('error', (err, ctx) => {
       log.error('server error', err, ctx)
     });

    When an error occurs and it is still possible to respond to the client, aka no data has been written to the socket, Koa will respond appropriately with a 500 "Internal Server Error". In either case @@ -200,7 +200,7 @@

    Error Handling

    A Context is created per request, and is referenced in middleware as the receiver, or the ctx identifier, as shown in the following snippet:

    -
    app.use(async ctx => {
    +
    app.use(async ctx => {
       ctx; // is the Context
       ctx.request; // is a koa Request
       ctx.response; // is a koa Response
    @@ -226,7 +226,7 @@ 

    ctx.response

    A koa Response object.

    ctx.state

    The recommended namespace for passing information through middleware and to your frontend views.

    -
    ctx.state.user = await User.find(id);

    ctx.app

    +
    ctx.state.user = await User.find(id);

    ctx.app

    Application instance reference.

    ctx.cookies.get(name, [options])

    Get cookie name with options:

    @@ -251,10 +251,10 @@

    ctx.throw([status], [msg], [properties

    Helper method to throw an error with a .status property defaulting to 500 that will allow Koa to respond appropriately. The following combinations are allowed:

    -
    ctx.throw(400);
    +
    ctx.throw(400);
     ctx.throw(400, 'name required');
     ctx.throw(400, 'name required', { user: user });

    For example ctx.throw(400, 'name required') is equivalent to:

    -
    const err = new Error('name required');
    +
    const err = new Error('name required');
     err.status = 400;
     err.expose = true;
     throw err;

    Note that these are user-level errors and are flagged with @@ -263,12 +263,12 @@

    ctx.throw([status], [msg], [properties error messages since you do not want to leak failure details.

    You may optionally pass a properties object which is merged into the error as-is, useful for decorating machine-friendly errors which are reported to the requester upstream.

    -
    ctx.throw(401, 'access_denied', { user: user });

    koa uses http-errors to create errors.

    +
    ctx.throw(401, 'access_denied', { user: user });

    koa uses http-errors to create errors.

    ctx.assert(value, [status], [msg], [properties])

    Helper method to throw an error similar to .throw() when !value. Similar to node's assert() method.

    -
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    koa uses http-assert for assertions.

    +
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    koa uses http-assert for assertions.

    ctx.respond

    To bypass Koa's built-in response handling, you may explicitly set ctx.respond = false;. Use this if you want to write to the raw res object instead of letting Koa handle the response for you.

    Note that using this is not supported by Koa. This may break intended functionality of Koa middleware and Koa itself. Using this property is considered a hack and is only a convenience to those wishing to use traditional fn(req, res) functions and middleware within Koa.

    @@ -357,10 +357,10 @@

    request.originalUrl

    Get request original URL.

    request.origin

    Get origin of URL, include protocol and host.

    -
    ctx.request.origin
    +
    ctx.request.origin
     // => http://example.com

    request.href

    Get full request URL, include protocol, host and url.

    -
    ctx.request.href;
    +
    ctx.request.href;
     // => http://example.com/foo/bar?q=1

    request.path

    Get request pathname.

    request.path=

    @@ -386,25 +386,25 @@

    request.URL

    Get WHATWG parsed URL object.

    request.type

    Get request Content-Type void of parameters such as "charset".

    -
    const ct = ctx.request.type;
    +
    const ct = ctx.request.type;
     // => "image/png"

    request.charset

    Get request charset when present, or undefined:

    -
    ctx.request.charset;
    +
    ctx.request.charset;
     // => "utf-8"

    request.query

    Get parsed query-string, returning an empty object when no query-string is present. Note that this getter does not support nested parsing.

    For example "color=blue&size=small":

    -
    {
    +
    {
       color: 'blue',
       size: 'small'
     }

    request.query=

    Set query-string to the given object. Note that this setter does not support nested objects.

    -
    ctx.query = { next: '/login' };

    request.fresh

    +
    ctx.query = { next: '/login' };

    request.fresh

    Check if a request cache is "fresh", aka the contents have not changed. This method is for cache negotiation between If-None-Match / ETag, and If-Modified-Since and Last-Modified. It should be referenced after setting one or more of these response headers.

    -
    // freshness check requires status 20x or 304
    +
    // freshness check requires status 20x or 304
     ctx.status = 200;
     ctx.set('ETag', '123');
     
    @@ -445,7 +445,7 @@ 

    request.is(types...)

    If there is no request body, null is returned. If there is no content type, or the match fails false is returned. Otherwise, it returns the matching content-type.

    -
    // With Content-Type: text/html; charset=utf-8
    +
    // With Content-Type: text/html; charset=utf-8
     ctx.is('html'); // => 'html'
     ctx.is('text/html'); // => 'text/html'
     ctx.is('text/*', 'text/html'); // => 'text/html'
    @@ -457,7 +457,7 @@ 

    request.is(types...)

    ctx.is('html'); // => false

    For example if you want to ensure that only images are sent to a given route:

    -
    if (ctx.is('image/*')) {
    +
    if (ctx.is('image/*')) {
       // process
     } else {
       ctx.throw(415, 'images only!');
    @@ -476,7 +476,7 @@ 

    request.accepts(types)

    Check if the given type(s) is acceptable, returning the best match when true, otherwise false. The type value may be one or more mime type string such as "application/json", the extension name such as "json", or an array ["json", "html", "text/plain"].

    -
    // Accept: text/html
    +
    // Accept: text/html
     ctx.accepts('html');
     // => "html"
     
    @@ -506,46 +506,46 @@ 

    request.accepts(types)

    ctx.accepts('json', 'html'); // => "json"

    You may call ctx.accepts() as many times as you like, or use a switch:

    -
    switch (ctx.accepts('json', 'html', 'text')) {
    +
    switch (ctx.accepts('json', 'html', 'text')) {
       case 'json': break;
       case 'html': break;
       case 'text': break;
       default: ctx.throw(406, 'json, html, or text only');
     }

    request.acceptsEncodings(encodings)

    Check if encodings are acceptable, returning the best match when true, otherwise false. Note that you should include identity as one of the encodings!

    -
    // Accept-Encoding: gzip
    +
    // Accept-Encoding: gzip
     ctx.acceptsEncodings('gzip', 'deflate', 'identity');
     // => "gzip"
     
     ctx.acceptsEncodings(['gzip', 'deflate', 'identity']);
     // => "gzip"

    When no arguments are given all accepted encodings are returned as an array:

    -
    // Accept-Encoding: gzip, deflate
    +
    // Accept-Encoding: gzip, deflate
     ctx.acceptsEncodings();
     // => ["gzip", "deflate", "identity"]

    Note that the identity encoding (which means no encoding) could be unacceptable if the client explicitly sends identity;q=0. Although this is an edge case, you should still handle the case where this method returns false.

    request.acceptsCharsets(charsets)

    Check if charsets are acceptable, returning the best match when true, otherwise false.

    -
    // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
    +
    // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
     ctx.acceptsCharsets('utf-8', 'utf-7');
     // => "utf-8"
     
     ctx.acceptsCharsets(['utf-7', 'utf-8']);
     // => "utf-8"

    When no arguments are given all accepted charsets are returned as an array:

    -
    // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
    +
    // Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
     ctx.acceptsCharsets();
     // => ["utf-8", "utf-7", "iso-8859-1"]

    request.acceptsLanguages(langs)

    Check if langs are acceptable, returning the best match when true, otherwise false.

    -
    // Accept-Language: en;q=0.8, es, pt
    +
    // Accept-Language: en;q=0.8, es, pt
     ctx.acceptsLanguages('es', 'en');
     // => "es"
     
     ctx.acceptsLanguages(['en', 'es']);
     // => "es"

    When no arguments are given all accepted languages are returned as an array:

    -
    // Accept-Language: en;q=0.8, es, pt
    +
    // Accept-Language: en;q=0.8, es, pt
     ctx.acceptsLanguages();
     // => ["es", "pt", "en"]

    request.idempotent

    Check if the request is idempotent.

    @@ -667,7 +667,7 @@

    Stream

    For example, you may not want this when setting the body as an HTTP stream in a proxy as it would destroy the underlying connection.

    See: https://github.com/koajs/koa/pull/612 for more information.

    Here's an example of stream error handling without automatically destroying the stream:

    -
    const PassThrough = require('stream').PassThrough;
    +
    const PassThrough = require('stream').PassThrough;
     
     app.use(async ctx => {
       ctx.body = someHTTPStream.on('error', ctx.onerror).pipe(PassThrough());
    @@ -675,23 +675,23 @@ 

    Stream

    The Content-Type is defaulted to application/json. This includes plain objects { foo: 'bar' } and arrays ['foo', 'bar'].

    response.get(field)

    Get a response header field value with case-insensitive field.

    -
    const etag = ctx.response.get('ETag');

    response.set(field, value)

    +
    const etag = ctx.response.get('ETag');

    response.set(field, value)

    Set response header field to value:

    -
    ctx.set('Cache-Control', 'no-cache');

    response.append(field, value)

    +
    ctx.set('Cache-Control', 'no-cache');

    response.append(field, value)

    Append additional header field with value val.

    -
    ctx.append('Link', '<http://127.0.0.1/>');

    response.set(fields)

    +
    ctx.append('Link', '<http://127.0.0.1/>');

    response.set(fields)

    Set several response header fields with an object:

    -
    ctx.set({
    +
    ctx.set({
       'Etag': '1234',
       'Last-Modified': date
     });

    response.remove(field)

    Remove header field.

    response.type

    Get response Content-Type void of parameters such as "charset".

    -
    const ct = ctx.type;
    +
    const ct = ctx.type;
     // => "image/png"

    response.type=

    Set response Content-Type via mime string or file extension.

    -
    ctx.type = 'text/plain; charset=utf-8';
    +
    ctx.type = 'text/plain; charset=utf-8';
     ctx.type = 'image/png';
     ctx.type = '.png';
     ctx.type = 'png';

    Note: when appropriate a charset is selected for you, for @@ -704,7 +704,7 @@

    response.is(types...)

    manipulate responses.

    For example, this is a middleware that minifies all HTML responses except for streams.

    -
    const minify = require('html-minifier');
    +
    const minify = require('html-minifier');
     
     app.use(async (ctx, next) => {
       await next();
    @@ -721,12 +721,12 @@ 

    response.is(types...)

    The string "back" is special-cased to provide Referrer support, when Referrer is not present alt or "/" is used.

    -
    ctx.redirect('back');
    +
    ctx.redirect('back');
     ctx.redirect('back', '/index.html');
     ctx.redirect('/login');
     ctx.redirect('http://google.com');

    To alter the default status of 302, simply assign the status before or after this call. To alter the body, assign it after this call:

    -
    ctx.status = 301;
    +
    ctx.status = 301;
     ctx.redirect('/cart');
     ctx.body = 'Redirecting to shopping cart';

    response.attachment([filename])

    Set Content-Disposition to "attachment" to signal the client @@ -740,10 +740,10 @@

    response.lastModified

    response.lastModified=

    Set the Last-Modified header as an appropriate UTC string. You can either set it as a Date or date string.

    -
    ctx.response.lastModified = new Date();

    response.etag=

    +
    ctx.response.lastModified = new Date();

    response.etag=

    Set the ETag of a response including the wrapped "s. Note that there is no corresponding response.etag getter.

    -
    ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');

    response.vary(field)

    +
    ctx.response.etag = crypto.createHash('md5').update(ctx.body).digest('hex');

    response.vary(field)

    Vary on field.

    response.flushHeaders()

    Flush any set headers, and begin the body.

    diff --git a/pug-options.js b/pug-options.js index 012ff2e..a674905 100644 --- a/pug-options.js +++ b/pug-options.js @@ -22,7 +22,7 @@ runkitContext.activated = false; - return "
    " + escaped + "
    " + script; + return "
    " + escaped + "
    " + script; }; renderer.html = function(text) { From ab41abf36b7c645079e794a2ea3d8daf0b5ed721 Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 30 Jul 2018 09:45:23 -0700 Subject: [PATCH 7/8] Update index.html. Reviewed by @tolmasky. --- index.html | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/index.html b/index.html index c6fc7f4..8788ce1 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,10 @@ -Koa - next generation web framework for node.js
    next generation web framework for node.js

    Introduction

    Koa is a new web framework designed by the team behind Express, +window.analytics.page();

    next generation web framework for node.js

    Introduction

    Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation -for web applications and APIs. Through leveraging generators Koa allows you +for web applications and APIs. By leveraging async functions, Koa allows you to ditch callbacks and greatly increase error-handling. Koa does not bundle any -middleware within core, and provides an elegant suite of methods that make +middleware within its core, and it provides an elegant suite of methods that make writing servers fast and enjoyable.

    Installation

    Koa requires node v7.6.0 or higher for ES2015 and async function support.

    @@ -202,8 +202,8 @@

    Error Handling

    snippet:

    app.use(async ctx => {
       ctx; // is the Context
    -  ctx.request; // is a koa Request
    -  ctx.response; // is a koa Response
    +  ctx.request; // is a Koa Request
    +  ctx.response; // is a Koa Response
     });

    Many of the context's accessors and methods simply delegate to their ctx.request or ctx.response equivalents for convenience, and are otherwise identical. For example ctx.type and ctx.length delegate to the response object, and ctx.path and ctx.method delegate to the request.

    @@ -221,9 +221,9 @@

    ctx.res

  • res.end()
  • ctx.request

    -

    A koa Request object.

    +

    A Koa Request object.

    ctx.response

    -

    A koa Response object.

    +

    A Koa Response object.

    ctx.state

    The recommended namespace for passing information through middleware and to your frontend views.

    ctx.state.user = await User.find(id);

    ctx.app

    @@ -233,7 +233,7 @@

    ctx.cookies.get(name, [options])

    • signed the cookie requested should be signed
    -

    koa uses the cookies module where options are simply passed.

    +

    Koa uses the cookies module where options are simply passed.

    ctx.cookies.set(name, value, [options])

    Set cookie name to value with options:

      @@ -246,7 +246,7 @@

      ctx.cookies.set(name, value, [optio
    • httpOnly server-accessible cookie, true by default
    • overwrite 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.
    -

    koa uses the cookies module where options are simply passed.

    +

    Koa uses the cookies module where options are simply passed.

    ctx.throw([status], [msg], [properties])

    Helper method to throw an error with a .status property defaulting to 500 that will allow Koa to respond appropriately. @@ -263,12 +263,12 @@

    ctx.throw([status], [msg], [properties error messages since you do not want to leak failure details.

    You may optionally pass a properties object which is merged into the error as-is, useful for decorating machine-friendly errors which are reported to the requester upstream.

    -
    ctx.throw(401, 'access_denied', { user: user });

    koa uses http-errors to create errors.

    +
    ctx.throw(401, 'access_denied', { user: user });

    Koa uses http-errors to create errors.

    ctx.assert(value, [status], [msg], [properties])

    Helper method to throw an error similar to .throw() when !value. Similar to node's assert() method.

    -
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    koa uses http-assert for assertions.

    +
    ctx.assert(ctx.state.user, 401, 'User not found. Please login!');

    Koa uses http-assert for assertions.

    ctx.respond

    To bypass Koa's built-in response handling, you may explicitly set ctx.respond = false;. Use this if you want to write to the raw res object instead of letting Koa handle the response for you.

    Note that using this is not supported by Koa. This may break intended functionality of Koa middleware and Koa itself. Using this property is considered a hack and is only a convenience to those wishing to use traditional fn(req, res) functions and middleware within Koa.

    From 35de0c41b7ab2a19b828f9d82653555972f1cf9d Mon Sep 17 00:00:00 2001 From: Francisco Ryan Tolmasky I Date: Mon, 30 Jul 2018 09:53:54 -0700 Subject: [PATCH 8/8] Re-remove highlight.js clientside. Reviewed by @tolmasky. --- index.html | 27 +++++++++++++-------------- views/index.jade | 1 - 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/index.html b/index.html index 8788ce1..33d5ae6 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,6 @@ Koa - next generation web framework for node.js
    next generation web framework for node.js

    Introduction

    Koa is a new web framework designed by the team behind Express, +window.analytics.page();

    next generation web framework for node.js

    Introduction

    Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs. By leveraging async functions, Koa allows you to ditch callbacks and greatly increase error-handling. Koa does not bundle any @@ -12,8 +12,8 @@

    $ nvm install 7
     $ npm i koa
     $ node my-koa-app.js

    Async Functions with Babel

    -

    To use async functions in Koa in versions of node < 7.6, we recommend using babel's require hook.

    -
    require('babel-core/register');
    +

    To use async functions in Koa in versions of node < 7.6, we recommend using babel's require hook.

    +
    require('babel-register');
     // require the rest of the app that needs to be transpiled after the hook
     const app = require('./app');

    To parse and transpile async functions, you should at a minimum have the transform-async-to-generator @@ -76,22 +76,21 @@

    Application

    const Koa = require('koa');
     const app = new Koa();
     
    -// x-response-time
    +// logger
     
     app.use(async (ctx, next) => {
    -  const start = Date.now();
       await next();
    -  const ms = Date.now() - start;
    -  ctx.set('X-Response-Time', `${ms}ms`);
    +  const rt = ctx.response.get('X-Response-Time');
    +  console.log(`${ctx.method} ${ctx.url} - ${rt}`);
     });
     
    -// logger
    +// x-response-time
     
     app.use(async (ctx, next) => {
       const start = Date.now();
       await next();
       const ms = Date.now() - start;
    -  console.log(`${ctx.method} ${ctx.url} - ${ms}`);
    +  ctx.set('X-Response-Time', `${ms}ms`);
     });
     
     // response
    @@ -128,7 +127,7 @@ 

    Application

  • app.subdomainOffset offset of .subdomains to ignore [2]
  • app.listen(...)

    -

    A Koa application is not a 1-to-1 representation of a HTTP server. +

    A Koa application is not a 1-to-1 representation of an HTTP server. One or more Koa applications may be mounted together to form larger applications with a single HTTP server.

    Create and return an HTTP server, passing the given arguments to @@ -149,7 +148,7 @@

    app.listen(...)

    https.createServer(app.callback()).listen(3001);

    app.callback()

    Return a callback function suitable for the http.createServer() method to handle a request. - You may also use this callback function to mount your koa app in a + You may also use this callback function to mount your Koa app in a Connect/Express app.

    app.use(function)

    Add the given middleware function to this application. See Middleware for @@ -163,7 +162,7 @@

    app.keys=

    app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');

    These keys may be rotated and are used when signing cookies with the { signed: true } option:

    ctx.cookies.set('name', 'tobi', { signed: true });

    app.context

    -

    app.context is the prototype from which ctx is created from. +

    app.context is the prototype from which ctx is created. You may add additional properties to ctx by editing app.context. This is useful for adding properties or methods to ctx to be used across your entire app, which may be more performant (no middleware) and/or easier (fewer require()s) @@ -176,11 +175,11 @@

    app.keys=

    });

    Note:

    • Many properties on ctx are defined using getters, setters, and Object.defineProperty(). You can only edit these properties (not recommended) by using Object.defineProperty() on app.context. See https://github.com/koajs/koa/issues/652.
    • -
    • Mounted apps currently use its parent's ctx and settings. Thus, mounted apps are really just groups of middleware.
    • +
    • Mounted apps currently use their parent's ctx and settings. Thus, mounted apps are really just groups of middleware.

    Error Handling

    By default outputs all errors to stderr unless app.silent is true. - The default error handler also won't outputs errors when err.status is 404 or err.expose is true. + The default error handler also won't output errors when err.status is 404 or err.expose is true. To perform custom error-handling logic such as centralized logging you can add an "error" event listener:

    app.on('error', err => {
       log.error('server error', err)
    diff --git a/views/index.jade b/views/index.jade
    index 10a682f..9a51896 100644
    --- a/views/index.jade
    +++ b/views/index.jade
    @@ -12,7 +12,6 @@ html
           window.analytics.load("9u0xff0b3k");
           window.analytics.page();
         script(src='stats.js')
    -    script(src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.10.0/highlight.min.js')
         script(src="https://embed.runkit.com", async, defer)
       body
         section#top