Switch to new repository
diff --git a/node_modules/json-schema-traverse/.eslintrc.yml b/node_modules/json-schema-traverse/.eslintrc.yml
new file mode 100644
index 0000000..ab1762d
--- /dev/null
+++ b/node_modules/json-schema-traverse/.eslintrc.yml
@@ -0,0 +1,27 @@
+extends: eslint:recommended
+env:
+  node: true
+  browser: true
+rules:
+  block-scoped-var: 2
+  complexity: [2, 13]
+  curly: [2, multi-or-nest, consistent]
+  dot-location: [2, property]
+  dot-notation: 2
+  indent: [2, 2, SwitchCase: 1]
+  linebreak-style: [2, unix]
+  new-cap: 2
+  no-console: [2, allow: [warn, error]]
+  no-else-return: 2
+  no-eq-null: 2
+  no-fallthrough: 2
+  no-invalid-this: 2
+  no-return-assign: 2
+  no-shadow: 1
+  no-trailing-spaces: 2
+  no-use-before-define: [2, nofunc]
+  quotes: [2, single, avoid-escape]
+  semi: [2, always]
+  strict: [2, global]
+  valid-jsdoc: [2, requireReturn: false]
+  no-control-regex: 0
diff --git a/node_modules/json-schema-traverse/.travis.yml b/node_modules/json-schema-traverse/.travis.yml
new file mode 100644
index 0000000..7ddce74
--- /dev/null
+++ b/node_modules/json-schema-traverse/.travis.yml
@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+  - "4"
+  - "6"
+  - "7"
+  - "8"
+after_script:
+  - coveralls < coverage/lcov.info
diff --git a/node_modules/json-schema-traverse/LICENSE b/node_modules/json-schema-traverse/LICENSE
new file mode 100644
index 0000000..7f15435
--- /dev/null
+++ b/node_modules/json-schema-traverse/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Evgeny Poberezkin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/node_modules/json-schema-traverse/README.md b/node_modules/json-schema-traverse/README.md
new file mode 100644
index 0000000..d5ccaf4
--- /dev/null
+++ b/node_modules/json-schema-traverse/README.md
@@ -0,0 +1,83 @@
+# json-schema-traverse
+Traverse JSON Schema passing each schema object to callback
+
+[![Build Status](https://travis-ci.org/epoberezkin/json-schema-traverse.svg?branch=master)](https://travis-ci.org/epoberezkin/json-schema-traverse)
+[![npm version](https://badge.fury.io/js/json-schema-traverse.svg)](https://www.npmjs.com/package/json-schema-traverse)
+[![Coverage Status](https://coveralls.io/repos/github/epoberezkin/json-schema-traverse/badge.svg?branch=master)](https://coveralls.io/github/epoberezkin/json-schema-traverse?branch=master)
+
+
+## Install
+
+```
+npm install json-schema-traverse
+```
+
+
+## Usage
+
+```javascript
+const traverse = require('json-schema-traverse');
+const schema = {
+  properties: {
+    foo: {type: 'string'},
+    bar: {type: 'integer'}
+  }
+};
+
+traverse(schema, {cb});
+// cb is called 3 times with:
+// 1. root schema
+// 2. {type: 'string'}
+// 3. {type: 'integer'}
+
+// Or:
+
+traverse(schema, {cb: {pre, post}});
+// pre is called 3 times with:
+// 1. root schema
+// 2. {type: 'string'}
+// 3. {type: 'integer'}
+//
+// post is called 3 times with:
+// 1. {type: 'string'}
+// 2. {type: 'integer'}
+// 3. root schema
+
+```
+
+Callback function `cb` is called for each schema object (not including draft-06 boolean schemas), including the root schema, in pre-order traversal. Schema references ($ref) are not resolved, they are passed as is.  Alternatively, you can pass a `{pre, post}` object as `cb`, and then `pre` will be called before traversing child elements, and `post` will be called after all child elements have been traversed.
+
+Callback is passed these parameters:
+
+- _schema_: the current schema object
+- _JSON pointer_: from the root schema to the current schema object
+- _root schema_: the schema passed to `traverse` object
+- _parent JSON pointer_: from the root schema to the parent schema object (see below)
+- _parent keyword_: the keyword inside which this schema appears (e.g. `properties`, `anyOf`, etc.)
+- _parent schema_: not necessarily parent object/array; in the example above the parent schema for `{type: 'string'}` is the root schema
+- _index/property_: index or property name in the array/object containing multiple schemas; in the example above for `{type: 'string'}` the property name is `'foo'`
+
+
+## Traverse objects in all unknown keywords
+
+```javascript
+const traverse = require('json-schema-traverse');
+const schema = {
+  mySchema: {
+    minimum: 1,
+    maximum: 2
+  }
+};
+
+traverse(schema, {allKeys: true, cb});
+// cb is called 2 times with:
+// 1. root schema
+// 2. mySchema
+```
+
+Without option `allKeys: true` callback will be called only with root schema.
+
+
+## License
+
+[MIT](https://github.com/epoberezkin/json-schema-traverse/blob/master/LICENSE)
diff --git a/node_modules/json-schema-traverse/index.js b/node_modules/json-schema-traverse/index.js
new file mode 100644
index 0000000..d4a18df
--- /dev/null
+++ b/node_modules/json-schema-traverse/index.js
@@ -0,0 +1,89 @@
+'use strict';
+
+var traverse = module.exports = function (schema, opts, cb) {
+  // Legacy support for v0.3.1 and earlier.
+  if (typeof opts == 'function') {
+    cb = opts;
+    opts = {};
+  }
+
+  cb = opts.cb || cb;
+  var pre = (typeof cb == 'function') ? cb : cb.pre || function() {};
+  var post = cb.post || function() {};
+
+  _traverse(opts, pre, post, schema, '', schema);
+};
+
+
+traverse.keywords = {
+  additionalItems: true,
+  items: true,
+  contains: true,
+  additionalProperties: true,
+  propertyNames: true,
+  not: true
+};
+
+traverse.arrayKeywords = {
+  items: true,
+  allOf: true,
+  anyOf: true,
+  oneOf: true
+};
+
+traverse.propsKeywords = {
+  definitions: true,
+  properties: true,
+  patternProperties: true,
+  dependencies: true
+};
+
+traverse.skipKeywords = {
+  default: true,
+  enum: true,
+  const: true,
+  required: true,
+  maximum: true,
+  minimum: true,
+  exclusiveMaximum: true,
+  exclusiveMinimum: true,
+  multipleOf: true,
+  maxLength: true,
+  minLength: true,
+  pattern: true,
+  format: true,
+  maxItems: true,
+  minItems: true,
+  uniqueItems: true,
+  maxProperties: true,
+  minProperties: true
+};
+
+
+function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
+  if (schema && typeof schema == 'object' && !Array.isArray(schema)) {
+    pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
+    for (var key in schema) {
+      var sch = schema[key];
+      if (Array.isArray(sch)) {
+        if (key in traverse.arrayKeywords) {
+          for (var i=0; i<sch.length; i++)
+            _traverse(opts, pre, post, sch[i], jsonPtr + '/' + key + '/' + i, rootSchema, jsonPtr, key, schema, i);
+        }
+      } else if (key in traverse.propsKeywords) {
+        if (sch && typeof sch == 'object') {
+          for (var prop in sch)
+            _traverse(opts, pre, post, sch[prop], jsonPtr + '/' + key + '/' + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop);
+        }
+      } else if (key in traverse.keywords || (opts.allKeys && !(key in traverse.skipKeywords))) {
+        _traverse(opts, pre, post, sch, jsonPtr + '/' + key, rootSchema, jsonPtr, key, schema);
+      }
+    }
+    post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
+  }
+}
+
+
+function escapeJsonPtr(str) {
+  return str.replace(/~/g, '~0').replace(/\//g, '~1');
+}
diff --git a/node_modules/json-schema-traverse/package.json b/node_modules/json-schema-traverse/package.json
new file mode 100644
index 0000000..4b3d654
--- /dev/null
+++ b/node_modules/json-schema-traverse/package.json
@@ -0,0 +1,69 @@
+{
+  "_from": "json-schema-traverse@^0.4.1",
+  "_id": "json-schema-traverse@0.4.1",
+  "_inBundle": false,
+  "_integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+  "_location": "/json-schema-traverse",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "range",
+    "registry": true,
+    "raw": "json-schema-traverse@^0.4.1",
+    "name": "json-schema-traverse",
+    "escapedName": "json-schema-traverse",
+    "rawSpec": "^0.4.1",
+    "saveSpec": null,
+    "fetchSpec": "^0.4.1"
+  },
+  "_requiredBy": [
+    "/ajv"
+  ],
+  "_resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+  "_shasum": "69f6a87d9513ab8bb8fe63bdb0979c448e684660",
+  "_spec": "json-schema-traverse@^0.4.1",
+  "author": {
+    "name": "Evgeny Poberezkin"
+  },
+  "bugs": {
+    "url": "https://github.com/epoberezkin/json-schema-traverse/issues"
+  },
+  "bundleDependencies": false,
+  "deprecated": false,
+  "description": "Traverse JSON Schema passing each schema object to callback",
+  "devDependencies": {
+    "coveralls": "^2.13.1",
+    "eslint": "^3.19.0",
+    "mocha": "^3.4.2",
+    "nyc": "^11.0.2",
+    "pre-commit": "^1.2.2"
+  },
+  "homepage": "https://github.com/epoberezkin/json-schema-traverse#readme",
+  "keywords": [
+    "JSON-Schema",
+    "traverse",
+    "iterate"
+  ],
+  "license": "MIT",
+  "main": "index.js",
+  "name": "json-schema-traverse",
+  "nyc": {
+    "exclude": [
+      "**/spec/**",
+      "node_modules"
+    ],
+    "reporter": [
+      "lcov",
+      "text-summary"
+    ]
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/epoberezkin/json-schema-traverse.git"
+  },
+  "scripts": {
+    "eslint": "eslint index.js spec",
+    "test": "npm run eslint && nyc npm run test-spec",
+    "test-spec": "mocha spec -R spec"
+  },
+  "version": "0.4.1"
+}
diff --git a/node_modules/json-schema-traverse/spec/.eslintrc.yml b/node_modules/json-schema-traverse/spec/.eslintrc.yml
new file mode 100644
index 0000000..3344da7
--- /dev/null
+++ b/node_modules/json-schema-traverse/spec/.eslintrc.yml
@@ -0,0 +1,6 @@
+parserOptions:
+  ecmaVersion: 6
+globals:
+  beforeEach: false
+  describe: false
+  it: false
diff --git a/node_modules/json-schema-traverse/spec/fixtures/schema.js b/node_modules/json-schema-traverse/spec/fixtures/schema.js
new file mode 100644
index 0000000..c51430c
--- /dev/null
+++ b/node_modules/json-schema-traverse/spec/fixtures/schema.js
@@ -0,0 +1,125 @@
+'use strict';
+
+var schema = {
+  additionalItems: subschema('additionalItems'),
+  items: subschema('items'),
+  contains: subschema('contains'),
+  additionalProperties: subschema('additionalProperties'),
+  propertyNames: subschema('propertyNames'),
+  not: subschema('not'),
+  allOf: [
+    subschema('allOf_0'),
+    subschema('allOf_1'),
+    {
+      items: [
+        subschema('items_0'),
+        subschema('items_1'),
+      ]
+    }
+  ],
+  anyOf: [
+    subschema('anyOf_0'),
+    subschema('anyOf_1'),
+  ],
+  oneOf: [
+    subschema('oneOf_0'),
+    subschema('oneOf_1'),
+  ],
+  definitions: {
+    foo: subschema('definitions_foo'),
+    bar: subschema('definitions_bar'),
+  },
+  properties: {
+    foo: subschema('properties_foo'),
+    bar: subschema('properties_bar'),
+  },
+  patternProperties: {
+    foo: subschema('patternProperties_foo'),
+    bar: subschema('patternProperties_bar'),
+  },
+  dependencies: {
+    foo: subschema('dependencies_foo'),
+    bar: subschema('dependencies_bar'),
+  },
+  required: ['foo', 'bar']
+};
+
+
+function subschema(keyword) {
+  var sch = {
+    properties: {},
+    additionalProperties: false,
+    additionalItems: false,
+    anyOf: [
+      {format: 'email'},
+      {format: 'hostname'}
+    ]
+  };
+  sch.properties['foo_' + keyword] = {title: 'foo'};
+  sch.properties['bar_' + keyword] = {title: 'bar'};
+  return sch;
+}
+
+
+module.exports = {
+  schema: schema,
+
+  // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
+  expectedCalls: [[schema, '', schema, undefined, undefined, undefined, undefined]]
+    .concat(expectedCalls('additionalItems'))
+    .concat(expectedCalls('items'))
+    .concat(expectedCalls('contains'))
+    .concat(expectedCalls('additionalProperties'))
+    .concat(expectedCalls('propertyNames'))
+    .concat(expectedCalls('not'))
+    .concat(expectedCallsChild('allOf', 0))
+    .concat(expectedCallsChild('allOf', 1))
+    .concat([
+      [schema.allOf[2], '/allOf/2', schema, '', 'allOf', schema, 2],
+      [schema.allOf[2].items[0], '/allOf/2/items/0', schema, '/allOf/2', 'items', schema.allOf[2], 0],
+      [schema.allOf[2].items[0].properties.foo_items_0, '/allOf/2/items/0/properties/foo_items_0', schema, '/allOf/2/items/0', 'properties', schema.allOf[2].items[0], 'foo_items_0'],
+      [schema.allOf[2].items[0].properties.bar_items_0, '/allOf/2/items/0/properties/bar_items_0', schema, '/allOf/2/items/0', 'properties', schema.allOf[2].items[0], 'bar_items_0'],
+      [schema.allOf[2].items[0].anyOf[0], '/allOf/2/items/0/anyOf/0', schema, '/allOf/2/items/0', 'anyOf', schema.allOf[2].items[0], 0],
+      [schema.allOf[2].items[0].anyOf[1], '/allOf/2/items/0/anyOf/1', schema, '/allOf/2/items/0', 'anyOf', schema.allOf[2].items[0], 1],
+
+      [schema.allOf[2].items[1], '/allOf/2/items/1', schema, '/allOf/2', 'items', schema.allOf[2], 1],
+      [schema.allOf[2].items[1].properties.foo_items_1, '/allOf/2/items/1/properties/foo_items_1', schema, '/allOf/2/items/1', 'properties', schema.allOf[2].items[1], 'foo_items_1'],
+      [schema.allOf[2].items[1].properties.bar_items_1, '/allOf/2/items/1/properties/bar_items_1', schema, '/allOf/2/items/1', 'properties', schema.allOf[2].items[1], 'bar_items_1'],
+      [schema.allOf[2].items[1].anyOf[0], '/allOf/2/items/1/anyOf/0', schema, '/allOf/2/items/1', 'anyOf', schema.allOf[2].items[1], 0],
+      [schema.allOf[2].items[1].anyOf[1], '/allOf/2/items/1/anyOf/1', schema, '/allOf/2/items/1', 'anyOf', schema.allOf[2].items[1], 1]
+    ])
+    .concat(expectedCallsChild('anyOf', 0))
+    .concat(expectedCallsChild('anyOf', 1))
+    .concat(expectedCallsChild('oneOf', 0))
+    .concat(expectedCallsChild('oneOf', 1))
+    .concat(expectedCallsChild('definitions', 'foo'))
+    .concat(expectedCallsChild('definitions', 'bar'))
+    .concat(expectedCallsChild('properties', 'foo'))
+    .concat(expectedCallsChild('properties', 'bar'))
+    .concat(expectedCallsChild('patternProperties', 'foo'))
+    .concat(expectedCallsChild('patternProperties', 'bar'))
+    .concat(expectedCallsChild('dependencies', 'foo'))
+    .concat(expectedCallsChild('dependencies', 'bar'))
+};
+
+
+function expectedCalls(keyword) {
+  return [
+    [schema[keyword], `/${keyword}`, schema, '', keyword, schema, undefined],
+    [schema[keyword].properties[`foo_${keyword}`], `/${keyword}/properties/foo_${keyword}`, schema, `/${keyword}`, 'properties', schema[keyword], `foo_${keyword}`],
+    [schema[keyword].properties[`bar_${keyword}`], `/${keyword}/properties/bar_${keyword}`, schema, `/${keyword}`, 'properties', schema[keyword], `bar_${keyword}`],
+    [schema[keyword].anyOf[0], `/${keyword}/anyOf/0`, schema, `/${keyword}`, 'anyOf', schema[keyword], 0],
+    [schema[keyword].anyOf[1], `/${keyword}/anyOf/1`, schema, `/${keyword}`, 'anyOf', schema[keyword], 1]
+  ];
+}
+
+
+function expectedCallsChild(keyword, i) {
+  return [
+    [schema[keyword][i], `/${keyword}/${i}`, schema, '', keyword, schema, i],
+    [schema[keyword][i].properties[`foo_${keyword}_${i}`], `/${keyword}/${i}/properties/foo_${keyword}_${i}`, schema, `/${keyword}/${i}`, 'properties', schema[keyword][i], `foo_${keyword}_${i}`],
+    [schema[keyword][i].properties[`bar_${keyword}_${i}`], `/${keyword}/${i}/properties/bar_${keyword}_${i}`, schema, `/${keyword}/${i}`, 'properties', schema[keyword][i], `bar_${keyword}_${i}`],
+    [schema[keyword][i].anyOf[0], `/${keyword}/${i}/anyOf/0`, schema, `/${keyword}/${i}`, 'anyOf', schema[keyword][i], 0],
+    [schema[keyword][i].anyOf[1], `/${keyword}/${i}/anyOf/1`, schema, `/${keyword}/${i}`, 'anyOf', schema[keyword][i], 1]
+  ];
+}
diff --git a/node_modules/json-schema-traverse/spec/index.spec.js b/node_modules/json-schema-traverse/spec/index.spec.js
new file mode 100644
index 0000000..c76b64f
--- /dev/null
+++ b/node_modules/json-schema-traverse/spec/index.spec.js
@@ -0,0 +1,171 @@
+'use strict';
+
+var traverse = require('../index');
+var assert = require('assert');
+
+describe('json-schema-traverse', function() {
+  var calls;
+
+  beforeEach(function() {
+    calls = [];
+  });
+
+  it('should traverse all keywords containing schemas recursively', function() {
+    var schema = require('./fixtures/schema').schema;
+    var expectedCalls = require('./fixtures/schema').expectedCalls;
+
+    traverse(schema, {cb: callback});
+    assert.deepStrictEqual(calls, expectedCalls);
+  });
+
+  describe('Legacy v0.3.1 API', function() {
+    it('should traverse all keywords containing schemas recursively', function() {
+      var schema = require('./fixtures/schema').schema;
+      var expectedCalls = require('./fixtures/schema').expectedCalls;
+
+      traverse(schema, callback);
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+
+    it('should work when an options object is provided', function() {
+      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
+      var schema = require('./fixtures/schema').schema;
+      var expectedCalls = require('./fixtures/schema').expectedCalls;
+
+      traverse(schema, {}, callback);
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+  });
+
+
+  describe('allKeys option', function() {
+    var schema = {
+      someObject: {
+        minimum: 1,
+        maximum: 2
+      }
+    };
+
+    it('should traverse objects with allKeys: true option', function() {
+      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
+      var expectedCalls = [
+        [schema, '', schema, undefined, undefined, undefined, undefined],
+        [schema.someObject, '/someObject', schema, '', 'someObject', schema, undefined]
+      ];
+
+      traverse(schema, {allKeys: true, cb: callback});
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+
+
+    it('should NOT traverse objects with allKeys: false option', function() {
+      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
+      var expectedCalls = [
+        [schema, '', schema, undefined, undefined, undefined, undefined]
+      ];
+
+      traverse(schema, {allKeys: false, cb: callback});
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+
+
+    it('should NOT traverse objects without allKeys option', function() {
+      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
+      var expectedCalls = [
+        [schema, '', schema, undefined, undefined, undefined, undefined]
+      ];
+
+      traverse(schema, {cb: callback});
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+
+
+    it('should NOT travers objects in standard keywords which value is not a schema', function() {
+      var schema2 = {
+        const: {foo: 'bar'},
+        enum: ['a', 'b'],
+        required: ['foo'],
+        another: {
+
+        },
+        patternProperties: {}, // will not traverse - no properties
+        dependencies: true, // will not traverse - invalid
+        properties: {
+          smaller: {
+            type: 'number'
+          },
+          larger: {
+            type: 'number',
+            minimum: {$data: '1/smaller'}
+          }
+        }
+      };
+
+      // schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex
+      var expectedCalls = [
+        [schema2, '', schema2, undefined, undefined, undefined, undefined],
+        [schema2.another, '/another', schema2, '', 'another', schema2, undefined],
+        [schema2.properties.smaller, '/properties/smaller', schema2, '', 'properties', schema2, 'smaller'],
+        [schema2.properties.larger, '/properties/larger', schema2, '', 'properties', schema2, 'larger'],
+      ];
+
+      traverse(schema2, {allKeys: true, cb: callback});
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+  });
+
+  describe('pre and post', function() {
+    var schema = {
+      type: 'object',
+      properties: {
+        name: {type: 'string'},
+        age: {type: 'number'}
+      }
+    };
+
+    it('should traverse schema in pre-order', function() {
+      traverse(schema, {cb: {pre}});
+      var expectedCalls = [
+        ['pre', schema, '', schema, undefined, undefined, undefined, undefined],
+        ['pre', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
+        ['pre', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
+      ];
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+
+    it('should traverse schema in post-order', function() {
+      traverse(schema, {cb: {post}});
+      var expectedCalls = [
+        ['post', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
+        ['post', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
+        ['post', schema, '', schema, undefined, undefined, undefined, undefined],
+      ];
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+
+    it('should traverse schema in pre- and post-order at the same time', function() {
+      traverse(schema, {cb: {pre, post}});
+      var expectedCalls = [
+        ['pre', schema, '', schema, undefined, undefined, undefined, undefined],
+        ['pre', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
+        ['post', schema.properties.name, '/properties/name', schema, '', 'properties', schema, 'name'],
+        ['pre', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
+        ['post', schema.properties.age, '/properties/age', schema, '', 'properties', schema, 'age'],
+        ['post', schema, '', schema, undefined, undefined, undefined, undefined],
+      ];
+      assert.deepStrictEqual(calls, expectedCalls);
+    });
+  });
+
+  function callback() {
+    calls.push(Array.prototype.slice.call(arguments));
+  }
+
+  function pre() {
+    calls.push(['pre'].concat(Array.prototype.slice.call(arguments)));
+  }
+
+  function post() {
+    calls.push(['post'].concat(Array.prototype.slice.call(arguments)));
+  }
+});