diff --git a/package.json b/package.json index a4c9feade..e334b4b36 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "feathers", "description": "Build Better APIs, Faster than Ever.", - "version": "2.1.3", + "version": "3.0.0-pre.0", "homepage": "http://feathersjs.com", "repository": { "type": "git", @@ -32,11 +32,12 @@ "release:patch": "npm version patch && npm publish", "release:minor": "npm version minor && npm publish", "release:major": "npm version major && npm publish", - "release:prerelease": "npm version prerelease && npm publish --tag pegasus", + "release:pre": "npm version prerelease && npm publish --tag pegasus", "compile": "rimraf lib/ && babel -d lib/ src/", "watch": "babel --watch -d lib/ src/", "lint": "eslint-if-supported semistandard --fix", "mocha": "mocha --opts mocha.opts", + "mocha:watch": "mocha --opts mocha.opts --watch", "coverage": "istanbul cover node_modules/mocha/bin/_mocha -- --opts mocha.opts", "test": "npm run compile && npm run lint && npm run coverage && nsp check" }, @@ -51,9 +52,7 @@ "dependencies": { "debug": "^2.6.0", "events": "^1.1.1", - "express": "^4.14.0", - "feathers-commons": "^0.8.7", - "rubberduck": "^1.1.1", + "feathers-commons": "^1.0.0-pre.1", "uberproto": "^1.2.0" }, "devDependencies": { @@ -61,21 +60,12 @@ "babel-core": "^6.21.0", "babel-plugin-add-module-exports": "^0.2.1", "babel-preset-es2015": "^6.18.0", - "body-parser": "^1.15.2", "eslint-if-supported": "^1.0.1", - "feathers-rest": "^1.5.3", - "feathers-socketio": "^2.0.0", "istanbul": "^1.1.0-alpha.1", "jshint": "^2.9.4", "mocha": "^3.2.0", "nsp": "^2.6.2", - "q": "^1.4.1", - "request": "^2.x", "rimraf": "^2.5.4", - "semistandard": "^10.0.0", - "socket.io-client": "^2.0.0" - }, - "browser": { - "./lib/index": "./lib/client/index" + "semistandard": "^10.0.0" } } diff --git a/src/application.js b/src/application.js index 8192845eb..8c43954d8 100644 --- a/src/application.js +++ b/src/application.js @@ -1,44 +1,95 @@ import makeDebug from 'debug'; import { stripSlashes } from 'feathers-commons'; import Uberproto from 'uberproto'; -import mixins from './mixins/index'; + +import events from './events'; +import hooks from './hooks'; const debug = makeDebug('feathers:application'); -const methods = ['find', 'get', 'create', 'update', 'patch', 'remove']; const Proto = Uberproto.extend({ create: null }); -export default { +const application = { init () { Object.assign(this, { - methods, - mixins: mixins(), + methods: [ 'find', 'get', 'create', 'update', 'patch', 'remove' ], + mixins: [], services: {}, providers: [], - _setup: false + _setup: false, + settings: {} }); + + this.configure(hooks()); + this.configure(events()); }, - service (location, service, options = {}) { - location = stripSlashes(location); + get (name) { + return this.settings[name]; + }, - if (!service) { - const current = this.services[location]; + set (name, value) { + this.settings[name] = value; + return this; + }, - if (typeof current === 'undefined' && typeof this.defaultService === 'function') { - return this.service(location, this.defaultService(location), options); - } + disable (name) { + this.settings[name] = false; + return this; + }, + + disabled (name) { + return !this.settings[name]; + }, - return current; + enable (name) { + this.settings[name] = true; + return this; + }, + + enabled (name) { + return !!this.settings[name]; + }, + + configure (fn) { + fn.call(this); + + return this; + }, + + service (path, service) { + if (typeof service !== 'undefined') { + throw new Error('Registering a new service with `app.service(path, service)` is no longer supported. Use `app.use(path, service)` instead.'); } - let protoService = Proto.extend(service); + const location = stripSlashes(path); + const current = this.services[location]; + + if (typeof current === 'undefined' && typeof this.defaultService === 'function') { + return this.use(`/${location}`, this.defaultService(location)) + .service(location); + } + + return current; + }, + + use (path, service, options = {}) { + const location = stripSlashes(path); + const hasMethod = methods => methods.some(name => + (service && typeof service[name] === 'function') + ); + + if (!hasMethod(this.methods.concat('setup'))) { + throw new Error(`Invalid service object passed for path \`${location}\``); + } + + const protoService = Proto.extend(service); debug(`Registering new service at \`${location}\``); // Add all the mixins - this.mixins.forEach(fn => fn.call(this, protoService)); + this.mixins.forEach(fn => fn.call(this, protoService, location, options)); if (typeof protoService._setup === 'function') { protoService._setup(this, location); @@ -46,7 +97,7 @@ export default { // Run the provider functions to register the service this.providers.forEach(provider => - provider.call(this, location, protoService, options) + provider.call(this, protoService, location, options) ); // If we ran setup already, set this service up explicitly @@ -55,38 +106,7 @@ export default { protoService.setup(this, location); } - return (this.services[location] = protoService); - }, - - use (location) { - let service; - let middleware = Array.from(arguments) - .slice(1) - .reduce(function (middleware, arg) { - if (typeof arg === 'function') { - middleware[service ? 'after' : 'before'].push(arg); - } else if (!service) { - service = arg; - } else { - throw new Error('invalid arg passed to app.use'); - } - return middleware; - }, { - before: [], - after: [] - }); - - const hasMethod = methods => methods.some(name => - (service && typeof service[name] === 'function') - ); - - // Check for service (any object with at least one service method) - if (hasMethod(['handle', 'set']) || !hasMethod(this.methods.concat('setup'))) { - return this._super.apply(this, arguments); - } - - // Any arguments left over are other middleware that we want to pass to the providers - this.service(location, service, { middleware }); + this.services[location] = protoService; return this; }, @@ -97,6 +117,7 @@ export default { const service = this.services[path]; debug(`Setting up service for \`${path}\``); + if (typeof service.setup === 'function') { service.setup(this, path); } @@ -105,24 +126,7 @@ export default { this._isSetup = true; return this; - }, - - // Express 3.x configure is gone in 4.x but we'll keep a more basic version - // That just takes a function in order to keep Feathers plugin configuration easier. - // Environment specific configurations should be done as suggested in the 4.x migration guide: - // https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x - configure (fn) { - fn.call(this); - - return this; - }, - - listen () { - const server = this._super.apply(this, arguments); - - this.setup(server); - debug('Feathers application listening'); - - return server; } }; + +export default application; diff --git a/src/client/express.js b/src/client/express.js deleted file mode 100644 index 6fb9c4701..000000000 --- a/src/client/express.js +++ /dev/null @@ -1,47 +0,0 @@ -import { EventEmitter } from 'events'; -import Proto from 'uberproto'; - -export default function () { - const app = { - settings: {}, - - get (name) { - return this.settings[name]; - }, - - set (name, value) { - this.settings[name] = value; - return this; - }, - - disable (name) { - this.settings[name] = false; - return this; - }, - - disabled (name) { - return !this.settings[name]; - }, - - enable (name) { - this.settings[name] = true; - return this; - }, - - enabled (name) { - return !!this.settings[name]; - }, - - use () { - throw new Error('Middleware functions can not be used in the Feathers client'); - }, - - listen () { - return {}; - } - }; - - Proto.mixin(EventEmitter.prototype, app); - - return app; -} diff --git a/src/client/index.js b/src/client/index.js deleted file mode 100644 index e2f7778d8..000000000 --- a/src/client/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import feathers from '../feathers'; -import express from './express'; - -export default function createApplication (...args) { - return feathers(express(...args)); -} - -createApplication.version = '2.0.1'; diff --git a/src/events.js b/src/events.js new file mode 100644 index 000000000..0b24dd1c2 --- /dev/null +++ b/src/events.js @@ -0,0 +1,82 @@ +import { EventEmitter } from 'events'; +import Proto from 'uberproto'; + +export function eventHook () { + return function (hook) { + const { app, service } = hook; + const eventName = app.eventMappings[hook.method]; + const isHookEvent = service._hookEvents && service._hookEvents.indexOf(eventName) !== -1; + + // If this event is not being sent yet and we are not in an error hook + if (eventName && isHookEvent && hook.type !== 'error') { + service.emit(eventName, hook.result, hook); + } + }; +} + +export function eventMixin (service) { + if (service._serviceEvents) { + return; + } + + const app = this; + // Indicates if the service is already an event emitter + const isEmitter = typeof service.on === 'function' && + typeof service.emit === 'function'; + + // If not, mix it in (the service is always an Uberproto object that has a .mixin) + if (typeof service.mixin === 'function' && !isEmitter) { + service.mixin(EventEmitter.prototype); + } + + // Define non-enumerable properties of + Object.defineProperties(service, { + // A list of all events that this service sends + _serviceEvents: { + value: Array.isArray(service.events) ? service.events.slice() : [] + }, + + // A list of events that should be handled through the event hooks + _hookEvents: { + value: [] + } + }); + + // `app.eventMappings` has the mapping from method name to event name + Object.keys(app.eventMappings).forEach(method => { + const event = app.eventMappings[method]; + const alreadyEmits = service._serviceEvents.indexOf(event) !== -1; + + // Add events for known methods to _serviceEvents and _hookEvents + // if the service indicated it does not send it itself yet + if (typeof service[method] === 'function' && !alreadyEmits) { + service._serviceEvents.push(event); + service._hookEvents.push(event); + } + }); +} + +export default function () { + return function () { + const app = this; + + // Mappings from service method to event name + Object.assign(app, { + eventMappings: { + create: 'created', + update: 'updated', + remove: 'removed', + patch: 'patched' + } + }); + + // Register the event hook + // `finally` hooks always run last after `error` and `after` hooks + app.hooks({ finally: eventHook() }); + + // Make the app an event emitter + Proto.mixin(EventEmitter.prototype, app); + + app.mixins.push(eventMixin); + }; +} diff --git a/src/feathers.js b/src/feathers.js deleted file mode 100644 index 82ca9b196..000000000 --- a/src/feathers.js +++ /dev/null @@ -1,14 +0,0 @@ -import Proto from 'uberproto'; -import Application from './application'; - -/** - * Create a Feathers application that extends Express. - * - * @return {Function} - * @api public - */ -export default function createApplication (app) { - Proto.mixin(Application, app); - app.init(); - return app; -} diff --git a/src/hooks.js b/src/hooks.js new file mode 100644 index 000000000..bdd915c3a --- /dev/null +++ b/src/hooks.js @@ -0,0 +1,118 @@ +import { + hooks as commons, validateArguments, isPromise +} from 'feathers-commons'; + +const { + createHookObject, getHooks, processHooks, enableHooks, makeArguments +} = commons; + +export function hookMixin (service) { + if (typeof service.hooks === 'function') { + return; + } + + const app = this; + const methods = app.methods; + const mixin = {}; + + // Add .hooks method and properties to the service + enableHooks(service, methods, app.hookTypes); + + // Assemble the mixin object that contains all "hooked" service methods + methods.forEach(method => { + if (typeof service[method] !== 'function') { + return; + } + + mixin[method] = function () { + const service = this; + const args = arguments; + + try { + validateArguments(method, args); + } catch (e) { + return Promise.reject(e); + } + + // A reference to the original method + const _super = service._super.bind(service); + // Create the hook object that gets passed through + const hookObject = createHookObject(method, args, { + type: 'before', // initial hook object type + service, + app + }); + const beforeHooks = getHooks(app, service, 'before', method); + + // Process all before hooks + return processHooks.call(service, beforeHooks, hookObject) + // Use the hook object to call the original method + .then(hookObject => { + // If `hookObject.result` is set, skip the original method + if (typeof hookObject.result !== 'undefined') { + return hookObject; + } + + // Otherwise, call it with arguments created from the hook object + const promise = _super(...makeArguments(hookObject)); + + if (!isPromise(promise)) { + throw new Error(`Service method '${hookObject.method}' for '${hookObject.path}' service must return a promise`); + } + + return promise.then(result => { + hookObject.result = result; + + return hookObject; + }); + }) + // Make a copy of hookObject from `before` hooks and update type + .then(hookObject => Object.assign({}, hookObject, { type: 'after' })) + // Run through all `after` hooks + .then(hookObject => { + const afterHooks = getHooks(app, service, 'after', method, true); + const finallyHooks = getHooks(app, service, 'finally', method, true); + const hookChain = afterHooks.concat(finallyHooks); + + return processHooks.call(service, hookChain, hookObject); + }) + // Finally, return the result + .then(hookObject => hookObject.result) + // Handle errors + .catch(error => { + const errorHooks = getHooks(app, service, 'error', method, true); + const finallyHooks = getHooks(app, service, 'finally', method, true); + const hookChain = errorHooks.concat(finallyHooks); + + const errorHookObject = Object.assign({}, error.hook || hookObject, { + type: 'error', + original: error.hook, + error + }); + + return processHooks + .call(service, hookChain, errorHookObject) + .then(hook => Promise.reject(hook.error)); + }); + }; + }); + + service.mixin(mixin); +} + +export default function () { + return function () { + const app = this; + + // We store a reference of all supported hook types on the app + // in case someone needs it + Object.assign(app, { + hookTypes: [ 'before', 'after', 'error', 'finally' ] + }); + + // Add functionality for hooks to be registered as app.hooks + enableHooks(app, app.methods, app.hookTypes); + + app.mixins.push(hookMixin); + }; +} diff --git a/src/hooks/commons.js b/src/hooks/commons.js deleted file mode 100644 index 63f1d8d1a..000000000 --- a/src/hooks/commons.js +++ /dev/null @@ -1,111 +0,0 @@ -import { each, hooks as utils } from 'feathers-commons'; - -export function isHookObject (hookObject) { - return typeof hookObject === 'object' && - typeof hookObject.method === 'string' && - typeof hookObject.type === 'string'; -} - -export function processHooks (hooks, initialHookObject) { - let hookObject = initialHookObject; - let updateCurrentHook = current => { - if (current) { - if (!isHookObject(current)) { - throw new Error(`${hookObject.type} hook for '${hookObject.method}' method returned invalid hook object`); - } - - hookObject = current; - } - - return hookObject; - }; - let promise = Promise.resolve(hookObject); - - // Go through all hooks and chain them into our promise - hooks.forEach(fn => { - const hook = fn.bind(this); - - if (hook.length === 2) { // function(hook, next) - promise = promise.then(hookObject => { - return new Promise((resolve, reject) => { - hook(hookObject, (error, result) => - error ? reject(error) : resolve(result)); - }); - }); - } else { // function(hook) - promise = promise.then(hook); - } - - // Use the returned hook object or the old one - promise = promise.then(updateCurrentHook); - }); - - return promise.catch(error => { - // Add the hook information to any errors - error.hook = hookObject; - throw error; - }); -} - -export function addHookTypes (target, types = ['before', 'after', 'error']) { - Object.defineProperty(target, '__hooks', { - value: {} - }); - - types.forEach(type => { - // Initialize properties where hook functions are stored - target.__hooks[type] = {}; - }); -} - -export function getHooks (app, service, type, method, appLast = false) { - const appHooks = app.__hooks[type][method] || []; - const serviceHooks = service.__hooks[type][method] || []; - - if (appLast) { - return serviceHooks.concat(appHooks); - } - - return appHooks.concat(serviceHooks); -} - -export function baseMixin (methods, ...objs) { - const mixin = { - hooks (allHooks) { - each(allHooks, (obj, type) => { - if (!this.__hooks[type]) { - throw new Error(`'${type}' is not a valid hook type`); - } - - const hooks = utils.convertHookData(obj); - - each(hooks, (value, method) => { - if (method !== 'all' && methods.indexOf(method) === -1) { - throw new Error(`'${method}' is not a valid hook method`); - } - }); - - methods.forEach(method => { - if (!(hooks[method] || hooks.all)) { - return; - } - - const myHooks = this.__hooks[type][method] || - (this.__hooks[type][method] = []); - - if (hooks.all) { - myHooks.push.apply(myHooks, hooks.all); - } - - if (hooks[method]) { - myHooks.push.apply(myHooks, hooks[method]); - } - }); - }); - - return this; - } - }; - - return Object.assign(mixin, ...objs); -} diff --git a/src/hooks/index.js b/src/hooks/index.js deleted file mode 100644 index 4d481cfe1..000000000 --- a/src/hooks/index.js +++ /dev/null @@ -1,139 +0,0 @@ -import Proto from 'uberproto'; -import { hooks as utils } from 'feathers-commons'; -import { addHookTypes, processHooks, baseMixin, getHooks } from './commons'; - -function isPromise (result) { - return typeof result !== 'undefined' && - typeof result.then === 'function'; -} - -function hookMixin (service) { - if (typeof service.hooks === 'function') { - return; - } - - const app = this; - const methods = app.methods; - const old = { - before: service.before, - after: service.after - }; - const mixin = baseMixin(methods, { - before (before) { - return this.hooks({ before }); - }, - - after (after) { - return this.hooks({ after }); - } - }); - - addHookTypes(service); - - methods.forEach(method => { - if (typeof service[method] !== 'function') { - return; - } - - mixin[method] = function () { - const service = this; - // A reference to the original method - const _super = this._super.bind(this); - // Additional data to add to the hook object - const hookData = { - app, - service, - get path () { - return Object.keys(app.services) - .find(path => app.services[path] === service); - } - }; - // Create the hook object that gets passed through - const hookObject = utils.hookObject(method, 'before', arguments, hookData); - // Get all hooks - const hooks = { - // For before hooks the app hooks will run first - before: getHooks(app, this, 'before', method), - // For after and error hooks the app hooks will run last - after: getHooks(app, this, 'after', method, true), - error: getHooks(app, this, 'error', method, true) - }; - - // Process all before hooks - return processHooks.call(this, hooks.before, hookObject) - // Use the hook object to call the original method - .then(hookObject => { - if (typeof hookObject.result !== 'undefined') { - return Promise.resolve(hookObject); - } - - return new Promise((resolve, reject) => { - const args = utils.makeArguments(hookObject); - // The method may not be normalized yet so we have to handle both - // ways, either by callback or by Promise - const callback = function (error, result) { - if (error) { - reject(error); - } else { - hookObject.result = result; - resolve(hookObject); - } - }; - - // We replace the callback with resolving the promise - args.splice(args.length - 1, 1, callback); - - const result = _super(...args); - - if (isPromise(result)) { - result.then(data => callback(null, data), callback); - } - }); - }) - // Make a copy of hookObject from `before` hooks and update type - .then(hookObject => Object.assign({}, hookObject, { type: 'after' })) - // Run through all `after` hooks - .then(processHooks.bind(this, hooks.after)) - // Finally, return the result - .then(hookObject => hookObject.result) - // Handle errors - .catch(error => { - const errorHook = Object.assign({}, error.hook || hookObject, { - type: 'error', - original: error.hook, - error - }); - - return processHooks - .call(this, hooks.error, errorHook) - .then(hook => Promise.reject(hook.error)); - }); - }; - }); - - service.mixin(mixin); - - // Before hooks that were registered in the service - if (old.before) { - service.before(old.before); - } - - // After hooks that were registered in the service - if (old.after) { - service.after(old.after); - } -} - -function configure () { - return function () { - const app = this; - - addHookTypes(app); - - Proto.mixin(baseMixin(app.methods), app); - - this.mixins.unshift(hookMixin); - }; -} - -export default configure; diff --git a/src/index.js b/src/index.js index d02de0baa..5d61a75b0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,17 @@ -import express from 'express'; -import feathers from './feathers'; +import Proto from 'uberproto'; -export default function createApplication (...args) { - return feathers(express(...args)); +import Application from './application'; + +export default function createApplication () { + const app = {}; + + // Mix in the base application + Proto.mixin(Application, app); + + app.init(); + + return app; } -// Expose all express methods (like express.engine()) -Object.assign(createApplication, express, { - version: require('../package.json').version -}); +// TODO use https://github.com/gnandretta/babel-plugin-version-inline +createApplication.version = '__VERSION__'; diff --git a/src/mixins/event.js b/src/mixins/event.js deleted file mode 100644 index 43431ec65..000000000 --- a/src/mixins/event.js +++ /dev/null @@ -1,59 +0,0 @@ -import rubberduck from 'rubberduck'; -import { EventEmitter } from 'events'; -import { hooks } from 'feathers-commons'; - -const hookObject = hooks.hookObject; -const eventMappings = { - create: 'created', - update: 'updated', - remove: 'removed', - patch: 'patched' -}; - -function upperCase (name) { - return name.charAt(0).toUpperCase() + name.substring(1); -} - -export default function (service) { - const app = this; - const isEmitter = typeof service.on === 'function' && - typeof service.emit === 'function'; - const emitter = service._rubberDuck = rubberduck.emitter(service); - - if (typeof service.mixin === 'function' && !isEmitter) { - service.mixin(EventEmitter.prototype); - } - - service._serviceEvents = Array.isArray(service.events) ? service.events.slice() : []; - - // Pass the Rubberduck error event through - // TODO deal with error events properly - emitter.on('error', function (errors) { - service.emit('serviceError', errors[0]); - }); - - Object.keys(eventMappings).forEach(method => { - const event = eventMappings[method]; - const alreadyEmits = service._serviceEvents.indexOf(event) !== -1; - - if (typeof service[method] === 'function' && !alreadyEmits) { - // The Rubberduck event name (e.g. afterCreate, afterUpdate or afterDestroy) - var eventName = `after${upperCase(method)}`; - service._serviceEvents.push(event); - // Punch the given method - emitter.punch(method, -1); - // Pass the event and error event through - emitter.on(eventName, function (results, args) { - if (!results[0]) { // callback without error - const hook = hookObject(method, 'after', args); - const data = Array.isArray(results[1]) ? results[1] : [ results[1] ]; - - hook.app = app; - data.forEach(current => service.emit(event, current, hook)); - } else { - service.emit('serviceError', results[0]); - } - }); - } - }); -} diff --git a/src/mixins/index.js b/src/mixins/index.js deleted file mode 100644 index 1583f63b2..000000000 --- a/src/mixins/index.js +++ /dev/null @@ -1,17 +0,0 @@ -export default function () { - const mixins = [ - require('./promise'), - require('./event'), - require('../hooks'), - require('./normalizer') - ]; - - // Override push to make sure that normalize is always the last - mixins.push = function () { - const args = [this.length - 1, 0].concat(Array.from(arguments)); - this.splice.apply(this, args); - return this.length; - }; - - return mixins; -} diff --git a/src/mixins/normalizer.js b/src/mixins/normalizer.js deleted file mode 100644 index 45cef9767..000000000 --- a/src/mixins/normalizer.js +++ /dev/null @@ -1,17 +0,0 @@ -import { getArguments } from 'feathers-commons'; - -export default function (service) { - if (typeof service.mixin === 'function') { - const mixin = {}; - - this.methods.forEach(method => { - if (typeof service[method] === 'function') { - mixin[method] = function () { - return this._super.apply(this, getArguments(method, arguments)); - }; - } - }); - - service.mixin(mixin); - } -} diff --git a/src/mixins/promise.js b/src/mixins/promise.js deleted file mode 100644 index 6ccb65b07..000000000 --- a/src/mixins/promise.js +++ /dev/null @@ -1,28 +0,0 @@ -function isPromise (result) { - return typeof result !== 'undefined' && - typeof result.then === 'function'; -} - -function wrapper () { - const result = this._super.apply(this, arguments); - const callback = arguments[arguments.length - 1]; - - if (typeof callback === 'function' && isPromise(result)) { - result.then(data => callback(null, data), error => callback(error)); - } - return result; -} - -export default function (service) { - if (typeof service.mixin === 'function') { - const mixin = {}; - - this.methods.forEach(method => { - if (typeof service[method] === 'function') { - mixin[method] = wrapper; - } - }); - - service.mixin(mixin); - } -} diff --git a/test/application.test.js b/test/application.test.js index 6450241ed..efba09475 100644 --- a/test/application.test.js +++ b/test/application.test.js @@ -1,61 +1,40 @@ import assert from 'assert'; -import path from 'path'; -import fs from 'fs'; import Proto from 'uberproto'; -import io from 'socket.io-client'; -import request from 'request'; -import https from 'https'; -import rest from 'feathers-rest'; -import socketio from 'feathers-socketio'; -import feathers from '../src/'; + +import feathers from '../src'; describe('Feathers application', () => { - it('is CommonJS compatible', () => { - assert.equal(typeof require('../lib/feathers'), 'function'); + it('initializes', () => { + const app = feathers(); + + assert.equal(typeof app.use, 'function'); + assert.equal(typeof app.service, 'function'); + assert.equal(typeof app.services, 'object'); }); - it('Express application should use express apps.', () => { + it('is an event emitter', done => { const app = feathers(); - const child = feathers(); + const original = { hello: 'world' }; - app.use('/path', child); - assert.equal(child.parent, app); + app.on('test', data => { + assert.deepEqual(original, data); + done(); + }); + + app.emit('test', original); }); - it('.use with invalid parameters', () => { + it('throws an error for old app.service(path, service)', () => { const app = feathers(); try { - app.use('/dummy', {}, {}); - assert.ok(false, 'Should never get here'); + app.service('/test', {}); } catch (e) { - assert.equal(e.message, 'invalid arg passed to app.use'); + assert.equal(e.message, 'Registering a new service with `app.service(path, service)` is no longer supported. Use `app.use(path, service)` instead.'); } }); - it('Register services and look them up with and without leading and trailing slashes.', () => { - const dummyService = { - find () { - // No need to implement this - } - }; - - const app = feathers().use('/dummy/service/', dummyService); - - app.listen(8012, () => app.use('/another/dummy/service/', dummyService)); - - assert.ok(typeof app.service('dummy/service').find === 'function', 'Could look up without slashes'); - assert.ok(typeof app.service('/dummy/service').find === 'function', 'Could look up with leading slash'); - assert.ok(typeof app.service('dummy/service/').find === 'function', 'Could look up with trailing slash'); - - app.on('listening', function () { - assert.ok(typeof app.service('another/dummy/service').find === 'function', 'Could look up without slashes'); - assert.ok(typeof app.service('/another/dummy/service').find === 'function', 'Could look up with leading slash'); - assert.ok(typeof app.service('another/dummy/service/').find === 'function', 'Could look up with trailing slash'); - }); - }); - - it('uses .defaultService if available', done => { + it('uses .defaultService if available', () => { const app = feathers(); assert.ok(!app.service('/todos/')); @@ -71,306 +50,239 @@ describe('Feathers application', () => { }; }; - app.service('/todos/').get('dishes').then(data => { + return app.service('/todos/').get('dishes').then(data => { assert.deepEqual(data, { id: 'dishes', description: 'You have to do dishes!' }); - done(); }); }); - it('Registers a service, wraps it, runs service.setup(), and adds the event and Promise mixin', done => { - const dummyService = { - setup (app, path) { - this.path = path; - }, - - create (data) { - return Promise.resolve(data); - } - }; - - const app = feathers().use('/dummy', dummyService); - const wrappedService = app.service('dummy'); - const server = app.listen(7887, function () { - app.use('/dumdum', dummyService); - const dynamicService = app.service('dumdum'); + it('providers are getting called with a service', () => { + const app = feathers(); + let providerRan = false; - assert.ok(wrappedService.path === 'dummy', 'Wrapped service setup method ran.'); - assert.ok(dynamicService.path === 'dumdum', 'Dynamic service setup method ran.'); + app.providers.push(function (service, location, options) { + assert.ok(service.dummy); + assert.equal(location, 'dummy'); + assert.deepEqual(options, {}); + providerRan = true; }); - assert.ok(Proto.isPrototypeOf(wrappedService), 'Service got wrapped as Uberproto object'); - assert.ok(typeof wrappedService.on === 'function', 'Wrapped service is an event emitter'); - - wrappedService.on('created', function (data) { - assert.equal(data.message, 'Test message', 'Got created event with test message'); - server.close(done); + app.use('/dummy', { + dummy: true, + get () {} }); - wrappedService.create({ - message: 'Test message' - }).then(data => - assert.equal(data.message, 'Test message', 'Got created event with test message')); + assert.ok(providerRan); + + app.setup(); }); - it('Initializes REST and SocketIO providers.', function (done) { - const todoService = { - get (name, params, callback) { - callback(null, { - id: name, - description: `You have to do ${name}!` - }); + describe('Services', () => { + it('calling .use with a non service object throws', () => { + const app = feathers(); + + try { + app.use('/bla', function () {}); + assert.ok(false, 'Should never get here'); + } catch (e) { + assert.equal(e.message, 'Invalid service object passed for path `bla`'); } - }; + }); - const app = feathers() - .configure(rest()) - .configure(socketio()) - .use('/todo', todoService); - const server = app.listen(6999).on('listening', () => { - const socket = io.connect('http://localhost:6999'); + it('registers and wraps a new service', () => { + const dummyService = { + setup (app, path) { + this.path = path; + }, - request('http://localhost:6999/todo/dishes', (error, response, body) => { - if (error) { - done(error); + create (data) { + return Promise.resolve(data); } - assert.ok(response.statusCode === 200, 'Got OK status code'); - const data = JSON.parse(body); - assert.equal(data.description, 'You have to do dishes!'); + }; - socket.emit('todo::get', 'laundry', {}, function (error, data) { - if (error) { - done(error); - } - assert.equal(data.description, 'You have to do laundry!'); + const app = feathers().use('/dummy', dummyService); + const wrappedService = app.service('dummy'); - socket.disconnect(); - server.close(done); - }); - }); + assert.ok(Proto.isPrototypeOf(wrappedService), 'Service got wrapped as Uberproto object'); + + return wrappedService.create({ + message: 'Test message' + }).then(data => assert.equal(data.message, 'Test message')); }); - }); - it('Uses custom middleware. (#21)', done => { - const todoService = { - get (name, params) { - return Promise.resolve({ - id: name, - description: `You have to do ${name}!`, - preService: params.preService - }); - } - }; + it('services can be re-used (#566)', done => { + const app1 = feathers(); + const app2 = feathers(); - const app = feathers() - .configure(rest()) - .use('/todo', function (req, res, next) { - req.feathers.preService = 'pre-service middleware'; - next(); - }, todoService, function (req, res, next) { - res.set('post-service', res.data.id); - next(); - }) - .use('/otherTodo', todoService); - - const server = app.listen(6995).on('listening', () => { - request('http://localhost:6995/todo/dishes', (error, response, body) => { - if (error) { - done(error); + app2.use('/dummy', { + create (data) { + return Promise.resolve(data); } - assert.ok(response.statusCode === 200, 'Got OK status code'); - const data = JSON.parse(body); - assert.equal(data.preService, 'pre-service middleware', 'Pre-service middleware updated response'); - assert.equal(response.headers['post-service'], 'dishes', 'Post-service middleware updated response'); - - request('http://localhost:6995/otherTodo/dishes', (error, response, body) => { - if (error) { - done(error); + }); + + const dummy = app2.service('dummy'); + + dummy.hooks({ + before: { + create (hook) { + hook.data.fromHook = true; } - assert.ok(response.statusCode === 200, 'Got OK status code'); - const data = JSON.parse(body); - assert.ok(!data.preService && !response.headers['post-service'], 'Custom middleware not run for different service.'); - server.close(done); + } + }); + + dummy.on('created', data => { + assert.deepEqual(data, { + message: 'Hi', + fromHook: true }); + done(); }); + + app1.use('/testing', app2.service('dummy')); + + app1.service('testing').create({ message: 'Hi' }); }); }); - it('REST and SocketIO with SSL server (#25)', done => { - // For more info on Request HTTPS settings see https://github.com/mikeal/request/issues/418 - // This needs to be set so that the SocektIO client can connect - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + // Copied from the Express tests (without special cases) + describe('Express app options compatibility', function () { + describe('.set()', () => { + it('should set a value', () => { + var app = feathers(); + app.set('foo', 'bar'); + assert.equal(app.get('foo'), 'bar'); + }); - const todoService = { - get (name, params, callback) { - callback(null, { - id: name, - description: `You have to do ${name}!` - }); - } - }; + it('should return the app', () => { + var app = feathers(); + assert.equal(app.set('foo', 'bar'), app); + }); - const app = feathers() - .configure(rest()) - .configure(socketio()).use('/secureTodos', todoService); + it('should return the app when undefined', () => { + var app = feathers(); + assert.equal(app.set('foo', undefined), app); + }); + }); - const httpsServer = https.createServer({ - key: fs.readFileSync(path.join(__dirname, 'resources', 'privatekey.pem')), - cert: fs.readFileSync(path.join(__dirname, 'resources', 'certificate.pem')), - rejectUnauthorized: false, - requestCert: false - }, app).listen(7889); + describe('.get()', () => { + it('should return undefined when unset', () => { + var app = feathers(); + assert.strictEqual(app.get('foo'), undefined); + }); - app.setup(httpsServer); + it('should otherwise return the value', () => { + var app = feathers(); + app.set('foo', 'bar'); + assert.equal(app.get('foo'), 'bar'); + }); + }); - httpsServer.on('listening', function () { - const socket = io('https://localhost:7889', { - secure: true, - port: 7889, - rejectUnauthorized: false + describe('.enable()', () => { + it('should set the value to true', () => { + var app = feathers(); + assert.equal(app.enable('tobi'), app); + assert.strictEqual(app.get('tobi'), true); }); + }); - request({ - url: 'https://localhost:7889/secureTodos/dishes', - strictSSL: false, - rejectUnhauthorized: false - }, function (error, response, body) { - if (error) { - done(error); - } - assert.ok(response.statusCode === 200, 'Got OK status code'); - const data = JSON.parse(body); - assert.equal(data.description, 'You have to do dishes!'); + describe('.disable()', () => { + it('should set the value to false', () => { + var app = feathers(); + assert.equal(app.disable('tobi'), app); + assert.strictEqual(app.get('tobi'), false); + }); + }); - socket.emit('secureTodos::get', 'laundry', {}, function (error, data) { - if (error) { - done(error); - } - assert.equal(data.description, 'You have to do laundry!'); + describe('.enabled()', () => { + it('should default to false', () => { + var app = feathers(); + assert.strictEqual(app.enabled('foo'), false); + }); - socket.disconnect(); - httpsServer.close(); - done(); - }); + it('should return true when set', () => { + var app = feathers(); + app.set('foo', 'bar'); + assert.strictEqual(app.enabled('foo'), true); }); }); - }); - - it('Returns the value of a promise. (#41)', function (done) { - let original = {}; - const todoService = { - get (name) { - original = { - id: name, - q: true, - description: `You have to do ${name}!` - }; - return Promise.resolve(original); - } - }; - const app = feathers() - .configure(rest()) - .use('/todo', todoService); + describe('.disabled()', () => { + it('should default to true', () => { + var app = feathers(); + assert.strictEqual(app.disabled('foo'), true); + }); - const server = app.listen(6880).on('listening', function () { - request('http://localhost:6880/todo/dishes', (error, response, body) => { - if (error) { - done(error); - } - assert.ok(response.statusCode === 200, 'Got OK status code'); - assert.deepEqual(original, JSON.parse(body)); - server.close(done); + it('should return false when set', () => { + var app = feathers(); + app.set('foo', 'bar'); + assert.strictEqual(app.disabled('foo'), false); }); }); }); - it('Calls _setup in order to set up custom routes with higher priority. (#86)', done => { - const todoService = { - get (name) { - return Promise.resolve({ - id: name, - q: true, - description: `You have to do ${name}!` - }); - }, - - _setup (app, path) { - app.get(`/${path}/count`, function (req, res) { - res.json({ counter: 10 }); - }); - } - }; - - const app = feathers() - .configure(rest()) - .use('/todo', todoService); + describe('.setup', () => { + it('app.setup calls .setup on all services', () => { + const app = feathers(); + let setupCount = 0; - const server = app.listen(8999).on('listening', function () { - request('http://localhost:8999/todo/dishes', (error, response, body) => { - if (error) { - done(error); + app.use('/dummy', { + setup (appRef, path) { + setupCount++; + assert.equal(appRef, app); + assert.equal(path, 'dummy'); } - assert.ok(response.statusCode === 200, 'Got OK status code'); - const data = JSON.parse(body); - assert.equal(data.description, 'You have to do dishes!'); - - request('http://localhost:8999/todo/count', (error, response, body) => { - if (error) { - done(error); - } - assert.ok(response.statusCode === 200, 'Got OK status code'); - const data = JSON.parse(body); - assert.equal(data.counter, 10); - server.close(done); - }); }); - }); - }); - it('mixins are unique to one application', function () { - const app = feathers(); - app.mixins.push(function () {}); - assert.equal(app.mixins.length, 5); + app.use('/simple', { + get (id) { + return Promise.resolve({ id }); + } + }); - const otherApp = feathers(); - otherApp.mixins.push(function () {}); - assert.equal(otherApp.mixins.length, 5); - }); + app.use('/dummy2', { + setup (appRef, path) { + setupCount++; + assert.equal(appRef, app); + assert.equal(path, 'dummy2'); + } + }); - it('initializes a service with only a setup method (#285)', done => { - const app = feathers(); + app.setup(); - app.use('/setup-only', { - setup (_app, path) { - assert.equal(_app, app); - assert.equal(path, 'setup-only'); - done(); - } + assert.ok(app._isSetup); + assert.equal(setupCount, 2); }); - app.setup(); - }); + it('registering a service after app.setup will be set up', () => { + const app = feathers(); - it('Event punching happens after normalization (#150)', done => { - const todoService = { - create (data) { - return Promise.resolve(data); - } - }; + app.setup(); + + app.use('/dummy', { + setup (appRef, path) { + assert.ok(app._isSetup); + assert.equal(appRef, app); + assert.equal(path, 'dummy'); + } + }); + }); - const app = feathers() - .configure(rest()) - .use('/todo', todoService); + it('calls _setup on a service right away', () => { + const app = feathers(); + let _setup = false; - const server = app.listen(7001).on('listening', function () { - app.service('todo').create({ - test: 'item' + app.use('/dummy', { + get () {}, + _setup (appRef, path) { + _setup = true; + assert.equal(appRef, app); + assert.equal(path, 'dummy'); + } }); - server.close(done); + assert.ok(_setup); }); }); }); diff --git a/test/client.test.js b/test/client.test.js deleted file mode 100644 index 9ec3eff84..000000000 --- a/test/client.test.js +++ /dev/null @@ -1,141 +0,0 @@ -import assert from 'assert'; -import Proto from 'uberproto'; -import feathers from '../src/client'; - -describe('Feathers universal client', () => { - it('is not an Express application', () => { - const app = feathers(); - // There may be some other better ways to verify this but it works for now - assert.ok(typeof app.render !== 'function'); - }); - - it('calling .use with a function throws', () => { - const app = feathers(); - - try { - app.use(function () {}); - assert.ok(false, 'Should never get here'); - } catch (e) { - assert.equal(e.message, 'Middleware functions can not be used in the Feathers client'); - } - }); - - it('.listen does nothing', () => { - assert.deepEqual(feathers().listen(), {}); - }); - - it('is an event emitter', done => { - const app = feathers(); - const original = { hello: 'world' }; - app.on('test', data => { - assert.deepEqual(original, data); - done(); - }); - - app.emit('test', original); - }); - - it('Registers a service, wraps it, runs service.setup(), and adds the event and Promise mixin', done => { - const dummyService = { - setup (app, path) { - this.path = path; - }, - - create (data) { - return Promise.resolve(data); - } - }; - - const app = feathers().use('/dummy', dummyService); - const wrappedService = app.service('dummy'); - - assert.ok(Proto.isPrototypeOf(wrappedService), 'Service got wrapped as Uberproto object'); - assert.ok(typeof wrappedService.on === 'function', 'Wrapped service is an event emitter'); - - wrappedService.on('created', function (data) { - assert.equal(data.message, 'Test message', 'Got created event with test message'); - done(); - }); - - wrappedService.create({ - message: 'Test message' - }).then(data => - assert.equal(data.message, 'Test message', 'Got created event with test message')); - }); - - // Copied from the Express tests (without special cases) - describe('Express app options compatibility', function () { - describe('.set()', () => { - it('should set a value', () => { - var app = feathers(); - app.set('foo', 'bar'); - assert.equal(app.get('foo'), 'bar'); - }); - - it('should return the app', () => { - var app = feathers(); - assert.equal(app.set('foo', 'bar'), app); - }); - - it('should return the app when undefined', () => { - var app = feathers(); - assert.equal(app.set('foo', undefined), app); - }); - }); - - describe('.get()', () => { - it('should return undefined when unset', () => { - var app = feathers(); - assert.strictEqual(app.get('foo'), undefined); - }); - - it('should otherwise return the value', () => { - var app = feathers(); - app.set('foo', 'bar'); - assert.equal(app.get('foo'), 'bar'); - }); - }); - - describe('.enable()', () => { - it('should set the value to true', () => { - var app = feathers(); - assert.equal(app.enable('tobi'), app); - assert.strictEqual(app.get('tobi'), true); - }); - }); - - describe('.disable()', () => { - it('should set the value to false', () => { - var app = feathers(); - assert.equal(app.disable('tobi'), app); - assert.strictEqual(app.get('tobi'), false); - }); - }); - - describe('.enabled()', () => { - it('should default to false', () => { - var app = feathers(); - assert.strictEqual(app.enabled('foo'), false); - }); - - it('should return true when set', () => { - var app = feathers(); - app.set('foo', 'bar'); - assert.strictEqual(app.enabled('foo'), true); - }); - }); - - describe('.disabled()', () => { - it('should default to true', () => { - var app = feathers(); - assert.strictEqual(app.disabled('foo'), true); - }); - - it('should return false when set', () => { - var app = feathers(); - app.set('foo', 'bar'); - assert.strictEqual(app.disabled('foo'), false); - }); - }); - }); -}); diff --git a/test/distributed.test.js b/test/distributed.test.js deleted file mode 100644 index bfbf6b536..000000000 --- a/test/distributed.test.js +++ /dev/null @@ -1,44 +0,0 @@ -import assert from 'assert'; -import io from 'socket.io-client'; -import socketio from 'feathers-socketio'; -import socketioClient from 'feathers-socketio/client'; -import rest from 'feathers-rest'; -import feathers from '../src/'; -import client from '../src/client'; - -describe('Distributed Feathers applications test', () => { - before(done => { - const app = feathers() - .configure(socketio()) - .use('todos', { - create (data) { - data.id = 42; - return Promise.resolve(data); - } - }); - - app.listen(8888, done); - }); - - it('passes created event between servers', done => { - const socket = io('http://localhost:8888'); - const remoteApp = client().configure(socketioClient(socket)); - const todo = { text: 'Created on alpha server', complete: false }; - const beta = feathers() - .configure(rest()) - .use('todos', remoteApp.service('todos')); - - beta.listen(9999, function () { - beta.service('todos').on('created', function (newTodo) { - assert.deepEqual(newTodo, { - id: 42, - text: 'Created on alpha server', - complete: false - }); - done(); - }); - - socket.emit('todos::create', todo); - }); - }); -}); diff --git a/test/events.test.js b/test/events.test.js new file mode 100644 index 000000000..e7e3e0300 --- /dev/null +++ b/test/events.test.js @@ -0,0 +1,158 @@ +import assert from 'assert'; +import { EventEmitter } from 'events'; + +import feathers from '../src'; + +describe('Service events', () => { + it('app is an event emitter', done => { + const app = feathers(); + + assert.equal(typeof app.on, 'function'); + + app.on('test', data => { + assert.deepEqual(data, { message: 'app' }); + done(); + }); + app.emit('test', { message: 'app' }); + }); + + it('works with service that is already an EventEmitter', done => { + const app = feathers(); + const service = new EventEmitter(); + + service.create = function (data) { + return Promise.resolve(data); + }; + + service.on('created', data => { + assert.deepEqual(data, { + message: 'testing' + }); + done(); + }); + + app.use('/emitter', service); + + app.service('emitter').create({ + message: 'testing' + }); + }); + + describe('emits event data on a service', () => { + it('.create and created', done => { + const app = feathers().use('/creator', { + create (data) { + return Promise.resolve(data); + } + }); + + const service = app.service('creator'); + + service.on('created', data => { + assert.deepEqual(data, { message: 'Hello' }); + done(); + }); + + service.create({ message: 'Hello' }); + }); + + it('.update and updated', done => { + const app = feathers().use('/creator', { + update (id, data) { + return Promise.resolve(Object.assign({ id }, data)); + } + }); + + const service = app.service('creator'); + + service.on('updated', data => { + assert.deepEqual(data, { id: 10, message: 'Hello' }); + done(); + }); + + service.update(10, { message: 'Hello' }); + }); + + it('.patch and patched', done => { + const app = feathers().use('/creator', { + patch (id, data) { + return Promise.resolve(Object.assign({ id }, data)); + } + }); + + const service = app.service('creator'); + + service.on('patched', data => { + assert.deepEqual(data, { id: 12, message: 'Hello' }); + done(); + }); + + service.patch(12, { message: 'Hello' }); + }); + + it('.remove and removed', done => { + const app = feathers().use('/creator', { + remove (id) { + return Promise.resolve({ id }); + } + }); + + const service = app.service('creator'); + + service.on('removed', data => { + assert.deepEqual(data, { id: 22 }); + done(); + }); + + service.remove(22); + }); + }); + + describe('event format', () => { + it('also emits the actual hook object', done => { + const app = feathers().use('/creator', { + create (data) { + return Promise.resolve(data); + } + }); + + const service = app.service('creator'); + + service.hooks({ + after (hook) { + hook.changed = true; + } + }); + + service.on('created', (data, hook) => { + assert.deepEqual(data, { message: 'Hi' }); + assert.ok(hook.changed); + assert.equal(hook.service, service); + assert.equal(hook.method, 'create'); + assert.equal(hook.type, 'after'); + done(); + }); + + service.create({ message: 'Hi' }); + }); + + it('events indicated by the service are not sent automatically', done => { + const app = feathers().use('/creator', { + events: [ 'created' ], + create (data) { + return Promise.resolve(data); + } + }); + + const service = app.service('creator'); + + service.on('created', data => { + assert.deepEqual(data, { message: 'custom event' }); + done(); + }); + + service.create({ message: 'hello' }) + .then(() => service.emit('created', { message: 'custom event' })); + }); + }); +}); diff --git a/test/hooks/after.test.js b/test/hooks/after.test.js index 4b0006428..d37a8f832 100644 --- a/test/hooks/after.test.js +++ b/test/hooks/after.test.js @@ -2,31 +2,32 @@ import assert from 'assert'; import feathers from '../../src'; -import hooks from '../../src/hooks'; -describe('.after hooks', () => { +describe('`after` hooks', () => { describe('function(hook)', () => { it('returning a non-hook object throws error', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id }); - }, + } + }); + const service = app.service('dummy'); + service.hooks({ after: { get () { return {}; } } }); - const service = app.service('dummy'); return service.get(10).catch(e => { assert.equal(e.message, 'after hook for \'get\' method returned invalid hook object'); }); }); - it('.after hooks can return a promise', done => { - const app = feathers().configure(hooks()).use('/dummy', { + it('.after hooks can return a promise', () => { + const app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id: id, @@ -40,32 +41,36 @@ describe('.after hooks', () => { }); const service = app.service('dummy'); - service.after({ - get (hook) { - hook.result.ran = true; - return Promise.resolve(hook); - }, + service.hooks({ + after: { + get (hook) { + hook.result.ran = true; + return Promise.resolve(hook); + }, - find () { - return Promise.reject(new Error('You can not see this')); + find () { + return Promise.reject(new Error('You can not see this')); + } } }); - service.get('laundry', {}, (error, data) => { + return service.get('laundry', {}).then(data => { assert.deepEqual(data, { id: 'laundry', description: 'You have to do laundry', ran: true }); - service.find({}, error => { + + return service.find({}).then(() => { + throw new Error('Should never get here'); + }).catch(error => { assert.equal(error.message, 'You can not see this'); - done(); }); }); }); it('.after hooks do not need to return anything', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id, description: `You have to do ${id}` @@ -78,13 +83,15 @@ describe('.after hooks', () => { }); const service = app.service('dummy'); - service.after({ - get (hook) { - hook.result.ran = true; - }, + service.hooks({ + after: { + get (hook) { + hook.result.ran = true; + }, - find () { - throw new Error('You can not see this'); + find () { + throw new Error('You can not see this'); + } } }); @@ -103,8 +110,17 @@ describe('.after hooks', () => { }); describe('function(hook, next)', () => { - it('gets mixed into a service and modifies data', done => { + it('gets mixed into a service and modifies data', () => { const dummyService = { + create (data, params) { + return Promise.resolve(data); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ after: { create (hook, next) { assert.equal(hook.type, 'after'); @@ -113,244 +129,250 @@ describe('.after hooks', () => { next(null, hook); } - }, - - create (data, params, callback) { - callback(null, data); } - }; - - const app = feathers().configure(hooks()).use('/dummy', dummyService); - const service = app.service('dummy'); + }); - service.create({ my: 'data' }, {}, (error, data) => { + return service.create({ my: 'data' }).then(data => { assert.deepEqual({ my: 'data', some: 'thing' }, data, 'Got modified data'); - done(); }); }); - it('also makes the app available at hook.app', done => { + it('also makes the app available at hook.app', () => { const dummyService = { + create (data) { + return Promise.resolve(data); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ after: { create (hook, next) { - hook.result.appPresent = typeof hook.app === 'function'; + hook.result.appPresent = typeof hook.app !== 'undefined'; assert.equal(hook.result.appPresent, true); next(null, hook); } - }, - - create (data, params, callback) { - callback(null, data); } - }; - - const app = feathers().configure(hooks()).use('/dummy', dummyService); - const service = app.service('dummy'); + }); - service.create({ my: 'data' }, {}, (error, data) => { + return service.create({ my: 'data' }).then(data => { assert.deepEqual({ my: 'data', appPresent: true }, data, 'The app was present in the hook.'); - done(); }); }); - it('returns errors', done => { + it('returns errors', () => { const dummyService = { + update (id, data) { + return Promise.resolve(data); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ after: { update (hook, next) { next(new Error('This did not work')); } - }, - - update (id, data, params, callback) { - callback(null, data); } - }; - - const app = feathers().configure(hooks()).use('/dummy', dummyService); - const service = app.service('dummy'); + }); - service.update(1, { my: 'data' }, {}, error => { + return service.update(1, { my: 'data' }).catch(error => { assert.ok(error, 'Got an error'); assert.equal(error.message, 'This did not work', 'Got expected error message from hook'); - done(); }); }); it('does not run after hook when there is an error', done => { const dummyService = { + remove () { + return Promise.reject(new Error('Error removing item')); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ after: { remove () { assert.ok(false, 'This should never get called'); } - }, - - remove (id, params, callback) { - callback(new Error('Error removing item')); } - }; - - const app = feathers().configure(hooks()).use('/dummy', dummyService); - const service = app.service('dummy'); + }); - service.remove(1, {}, error => { + service.remove(1, {}).catch(error => { assert.ok(error, 'Got error'); assert.equal(error.message, 'Error removing item', 'Got error message from service'); done(); }); }); - it('adds .after() and chains multiple hooks for the same method', done => { + it('adds .after() and chains multiple hooks for the same method', () => { const dummyService = { - create (data, params, callback) { - callback(null, data); + create (data) { + return Promise.resolve(data); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.after({ - create (hook, next) { - hook.result.some = 'thing'; - next(); + service.hooks({ + after: { + create (hook, next) { + hook.result.some = 'thing'; + next(); + } } }); - service.after({ - create (hook, next) { - hook.result.other = 'stuff'; + service.hooks({ + after: { + create (hook, next) { + hook.result.other = 'stuff'; - next(); + next(); + } } }); - service.create({ my: 'data' }, {}, (error, data) => { + service.create({ my: 'data' }).then(data => { assert.deepEqual({ my: 'data', some: 'thing', other: 'stuff' }, data, 'Got modified data'); - done(); }); }); - it('chains multiple after hooks using array syntax', done => { + it('chains multiple after hooks using array syntax', () => { const dummyService = { - create (data, params, callback) { - callback(null, data); + create (data) { + return Promise.resolve(data); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.after({ - create: [ - function (hook, next) { - hook.result.some = 'thing'; - next(); - }, - function (hook, next) { - hook.result.other = 'stuff'; - - next(); - } - ] + service.hooks({ + after: { + create: [ + function (hook, next) { + hook.result.some = 'thing'; + next(); + }, + function (hook, next) { + hook.result.other = 'stuff'; + + next(); + } + ] + } }); - service.create({ my: 'data' }, {}, (error, data) => { + return service.create({ my: 'data' }).then(data => { assert.deepEqual({ my: 'data', some: 'thing', other: 'stuff' }, data, 'Got modified data'); - done(); }); }); - it('.after hooks run in the correct order (#13)', done => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id }); + it('.after hooks run in the correct order (#13)', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); } }); const service = app.service('dummy'); - service.after({ - get (hook, next) { - hook.result.items = ['first']; - next(); + service.hooks({ + after: { + get (hook, next) { + hook.result.items = ['first']; + next(); + } } }); - service.after({ - get: [ - function (hook, next) { - hook.result.items.push('second'); - next(); - }, - function (hook, next) { - hook.result.items.push('third'); - next(); - } - ] + service.hooks({ + after: { + get: [ + function (hook, next) { + hook.result.items.push('second'); + next(); + }, + function (hook, next) { + hook.result.items.push('third'); + next(); + } + ] + } }); - service.get(10, {}, (error, data) => { + service.get(10).then(data => { assert.deepEqual(data.items, ['first', 'second', 'third']); - done(error); }); }); - it('after all hooks (#11)', done => { - const app = feathers().configure(hooks()).use('/dummy', { - after: { - all (hook, next) { - hook.result.afterAllObject = true; - next(); - } - }, - - get (id, params, callback) { - callback(null, { + it('after all hooks (#11)', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id: id, items: [] }); }, - find (params, callback) { - callback(null, []); + find () { + return Promise.resolve([]); } }); const service = app.service('dummy'); - service.after([ - function (hook, next) { - hook.result.afterAllMethodArray = true; - next(); + service.hooks({ + after: { + all (hook, next) { + hook.result.afterAllObject = true; + next(); + } } - ]); + }); - service.find({}, (error, data) => { + service.hooks({ + after: [ + function (hook, next) { + hook.result.afterAllMethodArray = true; + next(); + } + ] + }); + + return service.find({}).then(data => { assert.ok(data.afterAllObject); assert.ok(data.afterAllMethodArray); - service.get(1, {}, (error, data) => { + return service.get(1, {}).then(data => { assert.ok(data.afterAllObject); assert.ok(data.afterAllMethodArray); - done(); }); }); }); - it('after hooks have service as context and keep it in service method (#17)', done => { - const app = feathers().configure(hooks()).use('/dummy', { + it('after hooks have service as context and keep it in service method (#17)', () => { + const app = feathers().use('/dummy', { number: 42, - get (id, params, callback) { - callback(null, { + get (id) { + return Promise.resolve({ id: id, number: this.number }); @@ -359,50 +381,49 @@ describe('.after hooks', () => { const service = app.service('dummy'); - service.after({ - get (hook, next) { - hook.result.test = this.number + 1; - next(); + service.hooks({ + after: { + get (hook, next) { + hook.result.test = this.number + 1; + next(); + } } }); - service.get(10, {}, (error, data) => { + service.get(10).then(data => { assert.deepEqual(data, { id: 10, number: 42, test: 43 }); - done(); }); }); it('can not call next() multiple times', () => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id }); + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); } }); const service = app.service('dummy'); - service.after({ - get: [ - function (hook, next) { - next(); - }, - - function (hook, next) { - next(); - next(); - } - ] + service.hooks({ + after: { + get: [ + function (hook, next) { + next(); + }, + + function (hook, next) { + next(); + next(); + } + ] + } }); - try { - service.get(10); - } catch (e) { - assert.deepEqual(e.message, `next() called multiple times for hook on 'get' method`); - } + return service.get(10); }); }); }); diff --git a/test/hooks/app.test.js b/test/hooks/app.test.js index 5cdeb72b9..df5fc354b 100644 --- a/test/hooks/app.test.js +++ b/test/hooks/app.test.js @@ -1,14 +1,12 @@ import assert from 'assert'; import feathers from '../../src'; -import hooks from '../../src/hooks'; describe('app.hooks', () => { let app; beforeEach(() => { app = feathers() - .configure(hooks()) .use('/todos', { get (id, params) { if (id === 'error') { diff --git a/test/hooks/before.test.js b/test/hooks/before.test.js index 04b47a83a..d6d32748e 100644 --- a/test/hooks/before.test.js +++ b/test/hooks/before.test.js @@ -2,23 +2,24 @@ import assert from 'assert'; import feathers from '../../src'; -import hooks from '../../src/hooks'; -describe('.before hooks', () => { +describe('`before` hooks', () => { describe('function([hook])', () => { it('returning a non-hook object throws error', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id }); - }, + } + }); + const service = app.service('dummy'); + service.hooks({ before: { get () { return {}; } } }); - const service = app.service('dummy'); return service.get(10).catch(e => { assert.equal(e.message, 'before hook for \'get\' method returned invalid hook object'); @@ -26,7 +27,7 @@ describe('.before hooks', () => { }); it('hooks in chain can be replaced', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id, description: `You have to do ${id}` @@ -34,24 +35,28 @@ describe('.before hooks', () => { } }); - const service = app.service('dummy').before({ - get: [ - function (hook) { - return Object.assign({}, hook, { - modified: true - }); - }, - function (hook) { - assert.ok(hook.modified); - } - ] + const service = app.service('dummy'); + + service.hooks({ + before: { + get: [ + function (hook) { + return Object.assign({}, hook, { + modified: true + }); + }, + function (hook) { + assert.ok(hook.modified); + } + ] + } }); return service.get('laundry'); }); it('.before hooks can return a promise', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id, params) { assert.ok(params.ran, 'Ran through promise hook'); @@ -66,18 +71,22 @@ describe('.before hooks', () => { } }); - const service = app.service('dummy').before({ - get: function (hook) { - return new Promise(resolve => { - hook.params.ran = true; - resolve(); - }); - }, + const service = app.service('dummy'); - remove () { - return new Promise((resolve, reject) => { - reject(new Error('This did not work')); - }); + service.hooks({ + before: { + get (hook) { + return new Promise(resolve => { + hook.params.ran = true; + resolve(); + }); + }, + + remove () { + return new Promise((resolve, reject) => { + reject(new Error('This did not work')); + }); + } } }); @@ -88,7 +97,7 @@ describe('.before hooks', () => { }); it('.before hooks do not need to return anything', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id, params) { assert.ok(params.ran, 'Ran through promise hook'); @@ -103,13 +112,17 @@ describe('.before hooks', () => { } }); - const service = app.service('dummy').before({ - get: function (hook) { - hook.params.ran = true; - }, + const service = app.service('dummy'); - remove () { - throw new Error('This did not work'); + service.hooks({ + before: { + get (hook) { + hook.params.ran = true; + }, + + remove () { + throw new Error('This did not work'); + } } }); @@ -120,7 +133,7 @@ describe('.before hooks', () => { }); it('.before hooks can set hook.result which will skip service method', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id) { assert.ok(false, 'This should never run'); return Promise.resolve({ id }); @@ -129,12 +142,14 @@ describe('.before hooks', () => { const service = app.service('dummy'); - service.before({ - get (hook) { - hook.result = { - id: hook.id, - message: 'Set from hook' - }; + service.hooks({ + before: { + get (hook) { + hook.result = { + id: hook.id, + message: 'Set from hook' + }; + } } }); @@ -148,23 +163,9 @@ describe('.before hooks', () => { }); describe('function(hook, next)', () => { - it('gets mixed into a service and modifies data', done => { + it('gets mixed into a service and modifies data', () => { const dummyService = { - before: { - create (hook, next) { - assert.equal(hook.type, 'before'); - - hook.data.modified = 'data'; - - Object.assign(hook.params, { - modified: 'params' - }); - - next(null, hook); - } - }, - - create (data, params, callback) { + create (data, params) { assert.deepEqual(data, { some: 'thing', modified: 'data' @@ -174,100 +175,116 @@ describe('.before hooks', () => { modified: 'params' }, 'Params modified'); - callback(null, data); + return Promise.resolve(data); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.create({ some: 'thing' }, {}, (error, data) => { - assert.ok(!error, 'No error'); + service.hooks({ + before: { + create (hook, next) { + assert.equal(hook.type, 'before'); + + hook.data.modified = 'data'; + + Object.assign(hook.params, { + modified: 'params' + }); + + next(null, hook); + } + } + }); + return service.create({ some: 'thing' }).then(data => { assert.deepEqual(data, { some: 'thing', modified: 'data' }, 'Data got modified'); - - done(); }); }); - it('contains the app object at hook.app', done => { + it('contains the app object at hook.app', () => { const someServiceConfig = { + create (data, params, callback) { + return Promise.resolve(data); + } + }; + + const app = feathers().use('/some-service', someServiceConfig); + const someService = app.service('some-service'); + + someService.hooks({ before: { create (hook, next) { - hook.data.appPresent = typeof hook.app === 'function'; + hook.data.appPresent = typeof hook.app !== 'undefined'; assert.equal(hook.data.appPresent, true); next(null, hook); } - }, - - create (data, params, callback) { - callback(null, data); } - }; - - const app = feathers().configure(hooks()).use('/some-service', someServiceConfig); - const someService = app.service('some-service'); + }); - someService.create({ some: 'thing' }, {}, (error, data) => { + return someService.create({ some: 'thing' }).then(data => { assert.deepEqual(data, { some: 'thing', appPresent: true }, 'App object was present'); - - done(); }); }); - it('passes errors', done => { + it('passes errors', () => { const dummyService = { - before: { - update (hook, next) { - next(new Error('You are not allowed to update')); - } - }, - update () { assert.ok(false, 'Never should be called'); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.update(1, {}, {}, error => { + service.hooks({ + before: { + update (hook, next) { + next(new Error('You are not allowed to update')); + } + } + }); + + return service.update(1, {}).catch(error => { assert.ok(error, 'Got an error'); assert.equal(error.message, 'You are not allowed to update', 'Got error message'); - done(); }); }); - it('calling back with no arguments uses the old ones', done => { + it('calling back with no arguments uses the old ones', () => { const dummyService = { - before: { - remove (hook, next) { - next(); - } - }, - - remove (id, params, callback) { + remove (id, params) { assert.equal(id, 1, 'Got id'); assert.deepEqual(params, { my: 'param' }); - callback(); + + return Promise.resolve({ id }); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.remove(1, { my: 'param' }, done); + service.hooks({ + before: { + remove (hook, next) { + next(); + } + } + }); + + return service.remove(1, { my: 'param' }); }); - it('adds .before() and chains multiple hooks for the same method', done => { + it('adds .hooks() and chains multiple hooks for the same method', () => { const dummyService = { - create (data, params, callback) { + create (data, params) { assert.deepEqual(data, { some: 'thing', modified: 'second data' @@ -277,38 +294,39 @@ describe('.before hooks', () => { modified: 'params' }, 'Params modified'); - callback(null, data); + return Promise.resolve(data); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.before({ - create (hook, next) { - hook.params.modified = 'params'; + service.hooks({ + before: { + create (hook, next) { + hook.params.modified = 'params'; - next(); + next(); + } } }); - service.before({ - create (hook, next) { - hook.data.modified = 'second data'; + service.hooks({ + before: { + create (hook, next) { + hook.data.modified = 'second data'; - next(); + next(); + } } }); - service.create({ some: 'thing' }, {}, error => { - assert.ok(!error, 'No error'); - done(); - }); + return service.create({ some: 'thing' }); }); - it('chains multiple before hooks using array syntax', done => { + it('chains multiple before hooks using array syntax', () => { const dummyService = { - create (data, params, callback) { + create (data, params) { assert.deepEqual(data, { some: 'thing', modified: 'second data' @@ -318,39 +336,39 @@ describe('.before hooks', () => { modified: 'params' }, 'Params modified'); - callback(null, data); + return Promise.resolve(data); } }; - const app = feathers().configure(hooks()).use('/dummy', dummyService); + const app = feathers().use('/dummy', dummyService); const service = app.service('dummy'); - service.before({ - create: [ - function (hook, next) { - hook.params.modified = 'params'; - - next(); - }, - function (hook, next) { - hook.data.modified = 'second data'; - - next(); - } - ] + service.hooks({ + before: { + create: [ + function (hook, next) { + hook.params.modified = 'params'; + + next(); + }, + function (hook, next) { + hook.data.modified = 'second data'; + + next(); + } + ] + } }); - service.create({ some: 'thing' }, {}, error => { - assert.ok(!error, 'No error'); - done(); - }); + return service.create({ some: 'thing' }); }); - it('.before hooks run in the correct order (#13)', done => { - const app = feathers().configure(hooks()).use('/dummy', { + it('.before hooks run in the correct order (#13)', () => { + const app = feathers().use('/dummy', { get (id, params, callback) { assert.deepEqual(params.items, ['first', 'second', 'third']); - callback(null, { + + return Promise.resolve({ id: id, items: [] }); @@ -359,73 +377,81 @@ describe('.before hooks', () => { const service = app.service('dummy'); - service.before({ - get (hook, next) { - hook.params.items = ['first']; - next(); + service.hooks({ + before: { + get (hook, next) { + hook.params.items = ['first']; + next(); + } } }); - service.before({ - get: [ - function (hook, next) { - hook.params.items.push('second'); - next(); - }, - function (hook, next) { - hook.params.items.push('third'); - next(); - } - ] + service.hooks({ + before: { + get: [ + function (hook, next) { + hook.params.items.push('second'); + next(); + }, + function (hook, next) { + hook.params.items.push('third'); + next(); + } + ] + } }); - service.get(10, {}, done); + return service.get(10); }); - it('before all hooks (#11)', done => { - const app = feathers().configure(hooks()).use('/dummy', { - before: { - all (hook, next) { - hook.params.beforeAllObject = true; - next(); - } - }, - - get (id, params, callback) { + it('before all hooks (#11)', () => { + const app = feathers().use('/dummy', { + get (id, params) { assert.ok(params.beforeAllObject); assert.ok(params.beforeAllMethodArray); - callback(null, { + + return Promise.resolve({ id: id, items: [] }); }, - find (params, callback) { + find (params) { assert.ok(params.beforeAllObject); assert.ok(params.beforeAllMethodArray); - callback(null, []); + + return Promise.resolve([]); } }); const service = app.service('dummy'); - service.before([ - function (hook, next) { - hook.params.beforeAllMethodArray = true; - next(); + service.hooks({ + before: { + all (hook, next) { + hook.params.beforeAllObject = true; + next(); + } } - ]); + }); - service.find({}, () => { - service.get(1, {}, done); + service.hooks({ + before: [ + function (hook, next) { + hook.params.beforeAllMethodArray = true; + next(); + } + ] }); + + return service.find(); }); - it('before hooks have service as context and keep it in service method (#17)', done => { - const app = feathers().configure(hooks()).use('/dummy', { + it('before hooks have service as context and keep it in service method (#17)', () => { + const app = feathers().use('/dummy', { number: 42, - get (id, params, callback) { - callback(null, { + get (id, params) { + return Promise.resolve({ id: id, number: this.number, test: params.test @@ -435,43 +461,46 @@ describe('.before hooks', () => { const service = app.service('dummy'); - service.before({ - get (hook, next) { - hook.params.test = this.number + 2; - next(); + service.hooks({ + before: { + get (hook, next) { + hook.params.test = this.number + 2; + next(); + } } }); - service.get(10, {}, (error, data) => { + return service.get(10).then(data => { assert.deepEqual(data, { id: 10, number: 42, test: 44 }); - done(); }); }); it('calling next() multiple times does not do anything', () => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id }); + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); } }); const service = app.service('dummy'); - service.before({ - get: [ - function (hook, next) { - next(); - }, - - function (hook, next) { - next(); - next(); - } - ] + service.hooks({ + before: { + get: [ + function (hook, next) { + next(); + }, + + function (hook, next) { + next(); + next(); + } + ] + } }); return service.get(10).then(data => { diff --git a/test/hooks/error.test.js b/test/hooks/error.test.js index e067c1fdd..ec29a1df1 100644 --- a/test/hooks/error.test.js +++ b/test/hooks/error.test.js @@ -1,12 +1,11 @@ import assert from 'assert'; import feathers from '../../src'; -import hooks from '../../src/hooks'; -describe('error hooks', () => { +describe('`error` hooks', () => { describe('on direct service method errors', () => { const errorMessage = 'Something else went wrong'; - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get () { return Promise.reject(new Error('Something went wrong')); } @@ -128,7 +127,7 @@ describe('error hooks', () => { let app, service; beforeEach(() => { - app = feathers().configure(hooks()).use('/dummy', { + app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id, text: `You have to do ${id}` @@ -140,8 +139,10 @@ describe('error hooks', () => { }); it('error in before hook', done => { - service.before(function () { - throw new Error(errorMessage); + service.hooks({ + before () { + throw new Error(errorMessage); + } }).hooks({ error (hook) { assert.equal(hook.error.hook.type, 'before', diff --git a/test/hooks/finally.test.js b/test/hooks/finally.test.js new file mode 100644 index 000000000..1623d552c --- /dev/null +++ b/test/hooks/finally.test.js @@ -0,0 +1,77 @@ +import assert from 'assert'; + +import feathers from '../../src'; + +describe('`finally` hooks', () => { + it('runs after `after` hooks, app level last', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); + } + }); + + app.hooks({ + finally (hook) { + hook.result.chain.push('app finally'); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + finally (hook) { + hook.result.chain.push('service finally'); + }, + after (hook) { + hook.result.chain = [ 'service after' ]; + } + }); + + return service.get(42).then(data => { + assert.deepEqual(data, { + id: 42, + chain: [ 'service after', 'service finally', 'app finally' ] + }); + }); + }); + + it('runs after `error` hooks, app level last', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.reject(new Error(`${id} is not the answer`)); + } + }); + + app.hooks({ + finally (hook) { + hook.error.chain.push('app finally'); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + finally (hook) { + hook.error.chain.push('service finally'); + } + }); + + service.hooks({ + error (hook) { + hook.error.chain = [ 'service error' ]; + } + }); + + return service.get(42).then( + () => assert(false, 'Should never get here'), + error => { + assert.deepEqual(error.chain, [ + 'service error', + 'service finally', + 'app finally' + ]); + assert.deepEqual(error.message, '42 is not the answer'); + } + ); + }); +}); diff --git a/test/hooks/hooks.test.js b/test/hooks/hooks.test.js index 00795d45d..fd3331a6e 100644 --- a/test/hooks/hooks.test.js +++ b/test/hooks/hooks.test.js @@ -1,25 +1,26 @@ import assert from 'assert'; import feathers from '../../src'; -import hooks from '../../src/hooks'; -describe('feathers-hooks', () => { - it('always turns service call into a promise (#28)', () => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id }); +describe('hooks basics', () => { + it('validates arguments', () => { + const app = feathers().use('/dummy', { + get (id, params) { + return Promise.resolve({ id, user: params.user }); } }); - const service = app.service('dummy'); + return app.service('dummy').get(1, {}, function () {}).catch(e => { + assert.equal(e.message, 'Callbacks are no longer supported. Use Promises or async/await instead.'); - return service.get(10).then(data => { - assert.deepEqual(data, { id: 10 }); + return app.service('dummy').get(); + }).catch(e => { + assert.equal(e.message, `An id must be provided to the 'get' method`); }); }); - it('works with services that return a promise (#28)', () => { - const app = feathers().configure(hooks()).use('/dummy', { + it('works with services that return a promise (feathers-hooks#28)', () => { + const app = feathers().use('/dummy', { get (id, params) { return Promise.resolve({ id, user: params.user }); } @@ -27,13 +28,16 @@ describe('feathers-hooks', () => { const service = app.service('dummy'); - service.before({ - get (hook) { - hook.params.user = 'David'; - } - }).after({ - get (hook) { - hook.result.after = true; + service.hooks({ + before: { + get (hook) { + hook.params.user = 'David'; + } + }, + after: { + get (hook) { + hook.result.after = true; + } } }); @@ -42,43 +46,8 @@ describe('feathers-hooks', () => { }); }); - it('dispatches events with data modified by hook', done => { - const app = feathers().configure(hooks()).use('/dummy', { - create (data) { - return Promise.resolve(data); - } - }); - - const service = app.service('dummy'); - - service.before({ - create (hook) { - hook.data.user = 'David'; - } - }).after({ - create (hook) { - hook.result.after = true; - } - }); - - service.once('created', function (data) { - try { - assert.deepEqual(data, { - test: true, - user: 'David', - after: true - }); - done(); - } catch (e) { - done(e); - } - }); - - service.create({ test: true }); - }); - it('has hook.app, hook.service and hook.path', done => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id) { return Promise.resolve({ id }); } @@ -104,21 +73,23 @@ describe('feathers-hooks', () => { }); it('does not error when result is null', () => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id }); + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); } }); const service = app.service('dummy'); - service.after({ - get: [ - function (hook) { - hook.result = null; - return hook; - } - ] + service.hooks({ + after: { + get: [ + function (hook) { + hook.result = null; + return hook; + } + ] + } }); return service.get(1) @@ -126,7 +97,7 @@ describe('feathers-hooks', () => { }); it('invalid type in .hooks throws error', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id, params, callback) { callback(null, { id, params }); } @@ -143,7 +114,7 @@ describe('feathers-hooks', () => { }); it('invalid hook method throws error', () => { - const app = feathers().configure(hooks()).use('/dummy', { + const app = feathers().use('/dummy', { get (id, params, callback) { callback(null, { id, params }); } @@ -161,51 +132,25 @@ describe('feathers-hooks', () => { } }); - it('.hooks and backwards compatibility methods chain their hooks', () => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id, params }); + it('registering an already hooked service works (#154)', () => { + const app = feathers().use('/dummy', { + get (id, params) { + return Promise.resolve({ id, params }); } }); - const makeHooks = name => { - return { - all (hook) { - hook.params.items.push(`${name}_all`); - }, - get (hook) { - hook.params.items.push(`${name}_get`); - } - }; - }; - - const service = app.service('dummy'); - - service.hooks({ before: makeHooks('hooks_before') }); - service.hooks({ before: makeHooks('hooks_before_1') }); - service.before(makeHooks('before')); - service.before(makeHooks('before_1')); - - return service.get('testing', { items: [] }) - .then(data => assert.deepEqual(data.params.items, [ - 'hooks_before_all', - 'hooks_before_get', - 'hooks_before_1_all', - 'hooks_before_1_get', - 'before_all', - 'before_get', - 'before_1_all', - 'before_1_get' - ])); + app.use('/dummy2', app.service('dummy')); }); - it('registering an already hooked service works (#154)', () => { - const app = feathers().configure(hooks()).use('/dummy', { - get (id, params, callback) { - callback(null, { id, params }); + it('not returning a promise errors', () => { + const app = feathers().use('/dummy', { + get () { + return {}; } }); - app.use('/dummy2', app.service('dummy')); + return app.service('dummy').get(1).catch(e => { + assert.equal(e.message, `Service method 'get' for 'dummy' service must return a promise`); + }); }); }); diff --git a/test/mixins/event.test.js b/test/mixins/event.test.js deleted file mode 100644 index e89130ea4..000000000 --- a/test/mixins/event.test.js +++ /dev/null @@ -1,251 +0,0 @@ -import assert from 'assert'; -import Proto from 'uberproto'; -import { EventEmitter } from 'events'; -import mixinEvent from '../../src/mixins/event'; - -const create = Proto.create; - -describe('Event mixin', () => { - it('initializes', () => { - const FixtureService = Proto.extend({ - setup (arg) { - return `Original setup: ${arg}`; - } - }); - - mixinEvent(FixtureService); - - assert.equal(typeof FixtureService.setup, 'function'); - assert.equal(typeof FixtureService.on, 'function'); - assert.equal(typeof FixtureService.emit, 'function'); - - const instance = create.call(FixtureService); - assert.equal('Original setup: Test', instance.setup('Test')); - assert.ok(instance._rubberDuck instanceof EventEmitter); - - const existingMethodsService = { - setup (arg) { - return `Original setup from object: ${arg}`; - } - }; - - Proto.mixin(EventEmitter.prototype, existingMethodsService); - - assert.equal('Original setup from object: Test', existingMethodsService.setup('Test')); - assert.equal(typeof existingMethodsService.on, 'function'); - }); - - it('serviceError', function (done) { - var FixtureService = Proto.extend({ - create (data, params, cb) { - cb(new Error('Something went wrong')); - } - }); - - const instance = create.call(FixtureService); - - mixinEvent(instance); - - instance.on('serviceError', function (error) { - assert.ok(error instanceof Error); - assert.equal(error.message, 'Something went wrong'); - done(); - }); - - instance.create({ name: 'Tester' }, {}, - error => assert.ok(error instanceof Error)); - }); - - it('created', done => { - const FixtureService = Proto.extend({ - create: function (data, params, callback) { - callback(null, { - id: 10, - name: data.name - }); - } - }); - - const instance = create.call(FixtureService); - - mixinEvent(instance); - - instance.on('created', function (data, args) { - assert.equal(data.id, 10); - assert.equal(data.name, 'Tester'); - assert.equal(args.data.name, 'Tester'); - assert.equal(args.params.custom, 'value'); - done(); - }); - - instance.create({ - name: 'Tester' - }, { - custom: 'value' - }, (error, data) => { - if (error) { - done(error); - } - assert.equal(data.id, 10); - }); - }); - - it('updated', done => { - const FixtureService = Proto.extend({ - update (id, data, params, cb) { - setTimeout(function () { - cb(null, { - id: id, - name: data.name - }); - }, 20); - } - }); - - const instance = create.call(FixtureService); - - mixinEvent(instance); - - instance.on('updated', function (data, args) { - assert.equal(data.id, 12); - assert.equal(data.name, 'Updated tester'); - assert.equal(args.id, 12); - assert.equal(args.data.name, 'Updated tester'); - assert.equal(args.params.custom, 'value'); - done(); - }); - - instance.update(12, { - name: 'Updated tester' - }, { - custom: 'value' - }, function (error, data) { - if (error) { - done(error); - } - assert.equal(data.id, 12); - }); - }); - - it('removed', done => { - const FixtureService = Proto.extend({ - remove (id, params, cb) { - setTimeout(function () { - cb(null, { - id: id - }); - }, 20); - } - }); - - const instance = create.call(FixtureService); - - mixinEvent(instance); - - instance.on('removed', function (data, args) { - assert.equal(data.id, 27); - assert.equal(args.id, 27); - assert.equal(args.params.custom, 'value'); - done(); - }); - - instance.remove(27, { - custom: 'value' - }, function (error, data) { - if (error) { - done(error); - } - assert.equal(data.id, 27); - }); - }); - - it('array event data emits multiple event', done => { - const fixture = [ - { id: 0 }, - { id: 1 }, - { id: 2 } - ]; - const FixtureService = Proto.extend({ - create (data, params, cb) { - setTimeout(function () { - cb(null, fixture); - }, 20); - } - }); - - const instance = create.call(FixtureService); - let counter = 0; - - mixinEvent(instance); - - instance.on('created', function (data) { - assert.equal(data.id, counter); - counter++; - if (counter === fixture.length) { - done(); - } - }); - - instance.create({}, {}, function () {}); - }); - - it('does not punch when service has an events list (#118)', done => { - const FixtureService = Proto.extend({ - events: [ 'created' ], - create (data, params, cb) { - setTimeout(function () { - cb(null, { - id: 10, - name: data.name - }); - }, 20); - } - }); - - FixtureService.mixin(EventEmitter.prototype); - - const instance = create.call(FixtureService); - - mixinEvent(instance); - - instance.on('created', function (data) { - assert.deepEqual(data, { custom: 'event' }); - done(); - }); - - instance.create({ - name: 'Tester' - }, {}, function (error, data) { - if (error) { - done(error); - } - assert.equal(data.id, 10); - instance.emit('created', { custom: 'event' }); - }); - }); - - it('sets hook.app', done => { - const FixtureService = Proto.extend({ - update (id, data, params, cb) { - setTimeout(function () { - cb(null, { - id: id, - name: data.name - }); - }, 20); - } - }); - - const instance = create.call(FixtureService); - const dummyApp = { isApp: true }; - - mixinEvent.call(dummyApp, instance); - - instance.on('updated', function (data, hook) { - assert.deepEqual(hook.app, dummyApp); - done(); - }); - - instance.update(12, { name: 'Updated tester' }, {}, function () {}); - }); -}); diff --git a/test/mixins/normalizer.test.js b/test/mixins/normalizer.test.js deleted file mode 100644 index e8511f961..000000000 --- a/test/mixins/normalizer.test.js +++ /dev/null @@ -1,57 +0,0 @@ -import assert from 'assert'; -import Proto from 'uberproto'; -import normalizer from '../../src/mixins/normalizer'; -import mixins from '../../src/mixins'; - -describe('Argument normalizer mixin', () => { - it('normalizer mixin is always the last to run', () => { - const arr = mixins(); - const dummy = function () { }; - - assert.equal(arr.length, 4); - - arr.push(dummy); - - assert.equal(arr[arr.length - 1], normalizer, 'Last mixin is still the normalizer'); - assert.equal(arr[arr.length - 2], dummy, 'Dummy got added before last'); - }); - - // The normalization is already tests in all variations in `getArguments` - // so we just so we only test two random samples - - it('normalizes .find without a callback', done => { - const context = { methods: ['find'] }; - const FixtureService = Proto.extend({ - find (params, callback) { - assert.ok(typeof callback === 'function'); - assert.equal(params.test, 'Here'); - done(); - } - }); - - normalizer.call(context, FixtureService); - - const instance = Proto.create.call(FixtureService); - - instance.find({ test: 'Here' }); - }); - - it('normalizes .update without params and callback', done => { - const context = { methods: ['update'] }; - const FixtureService = Proto.extend({ - update (id, data, params, callback) { - assert.equal(id, 1); - assert.ok(typeof callback === 'function'); - assert.deepEqual(data, { test: 'Here' }); - assert.deepEqual(params, {}); - done(); - } - }); - - normalizer.call(context, FixtureService); - - const instance = Proto.create.call(FixtureService); - - instance.update(1, { test: 'Here' }); - }); -}); diff --git a/test/mixins/promise.test.js b/test/mixins/promise.test.js deleted file mode 100644 index 245609226..000000000 --- a/test/mixins/promise.test.js +++ /dev/null @@ -1,77 +0,0 @@ -import assert from 'assert'; -import Proto from 'uberproto'; -import mixin from '../../src/mixins/promise'; - -const create = Proto.create; - -describe('Promises/A+ mixin', () => { - it('Calls a callback when a promise is returned from the original service', done => { - // A dummy context (this will normally be the application) - const context = { methods: ['get'] }; - const FixtureService = Proto.extend({ - get: function (id) { - return Promise.resolve({ - id: id, - description: `You have to do ${id}` - }); - } - }); - - mixin.call(context, FixtureService); - - const instance = create.call(FixtureService); - instance.get('dishes', {}, function (error, data) { - if (error) { - done(error); - } - assert.deepEqual(data, { - id: 'dishes', - description: 'You have to do dishes' - }); - done(); - }); - }); - - it('calls back with an error for a failing deferred', done => { - // A dummy context (this will normally be the application) - var context = { - methods: ['get'] - }; - var FixtureService = Proto.extend({ - get: function () { - return Promise.reject(new Error('Something went wrong')); - } - }); - - mixin.call(context, FixtureService); - - var instance = create.call(FixtureService); - instance.get('dishes', {}, function (error) { - assert.ok(error); - assert.equal(error.message, 'Something went wrong'); - done(); - }); - }); - - it('does not try to call the callback if it does not exist', function (done) { - // A dummy context (this will normally be the application) - const context = { methods: ['create'] }; - const FixtureService = Proto.extend({ - create (data) { - return Promise.resolve(data); - } - }); - const original = { - id: 'laundry', - description: 'You have to do laundry' - }; - - mixin.call(context, FixtureService); - - const instance = create.call(FixtureService); - instance.create(original, {}).then(data => { - assert.deepEqual(data, original); - done(); - }); - }); -}); diff --git a/test/resources/certificate.pem b/test/resources/certificate.pem deleted file mode 100644 index 673564507..000000000 --- a/test/resources/certificate.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICmzCCAgQCCQDugFqITnU/sDANBgkqhkiG9w0BAQUFADCBkTELMAkGA1UEBhMC -Q0ExEDAOBgNVBAgTB0FsYmVydGExEDAOBgNVBAcTB0NhbGdhcnkxETAPBgNVBAoT -CEZlYXRoZXJzMREwDwYDVQQLEwhGZWF0aGVyczETMBEGA1UEAxMKRmVhdGhlcnNK -UzEjMCEGCSqGSIb3DQEJARYUaGVsbG9AZmVhdGhlcnNqcy5jb20wHhcNMTQwMTA0 -MDIwNTUyWhcNMTQwMjAzMDIwNTUyWjCBkTELMAkGA1UEBhMCQ0ExEDAOBgNVBAgT -B0FsYmVydGExEDAOBgNVBAcTB0NhbGdhcnkxETAPBgNVBAoTCEZlYXRoZXJzMREw -DwYDVQQLEwhGZWF0aGVyczETMBEGA1UEAxMKRmVhdGhlcnNKUzEjMCEGCSqGSIb3 -DQEJARYUaGVsbG9AZmVhdGhlcnNqcy5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0A -MIGJAoGBALixfLwrvDDYAaaU62oycz8zwUpxCguyyXyhVDN/KMmP/I+HfkbcIrqj -tW0jbpRWiLhn5cw4K/cUTkfMj4AwaN5t2zq0FVFJdIddLxzuamyJLJFZfs5sPYWt -X6morPcu9RM7jwb3R1V852XjVWUj8neUAu7eUzKoSQ575kHsnKrdAgMBAAEwDQYJ -KoZIhvcNAQEFBQADgYEATVlxNPkSgkqBF4foUYNGnkvaiwhd88Mh/Ya3T3EnknF9 -Gz6KrlwWDDI8MkPmqabT2Ijg3LSec7WV+C8SETVFbWLOGV6N1ZVfodFzJ7EKMz5e -VvEIKnHfHpYOEa21E5u02+OfKahtW37eTEVmvcV67vYmW4HNa5QSZ5qfrrqcUhc= ------END CERTIFICATE----- diff --git a/test/resources/certrequest.csr b/test/resources/certrequest.csr deleted file mode 100644 index 41f83b275..000000000 --- a/test/resources/certrequest.csr +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIB0jCCATsCAQAwgZExCzAJBgNVBAYTAkNBMRAwDgYDVQQIEwdBbGJlcnRhMRAw -DgYDVQQHEwdDYWxnYXJ5MREwDwYDVQQKEwhGZWF0aGVyczERMA8GA1UECxMIRmVh -dGhlcnMxEzARBgNVBAMTCkZlYXRoZXJzSlMxIzAhBgkqhkiG9w0BCQEWFGhlbGxv -QGZlYXRoZXJzanMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4sXy8 -K7ww2AGmlOtqMnM/M8FKcQoLssl8oVQzfyjJj/yPh35G3CK6o7VtI26UVoi4Z+XM -OCv3FE5HzI+AMGjebds6tBVRSXSHXS8c7mpsiSyRWX7ObD2FrV+pqKz3LvUTO48G -90dVfOdl41VlI/J3lALu3lMyqEkOe+ZB7Jyq3QIDAQABoAAwDQYJKoZIhvcNAQEF -BQADgYEAFN1xm2Jc5EwDsiJwjUQkVCYLfAPz8FxLx8XCY7JugPCZWxeJ3w9C3Ymz -hET//7uxNg6q7EO9CI33vP5eOdI8oC8XQffh4GzCoSrmGrKpHSqVh3zN/rCoB4BY -f4nJofTka5iENjMdA0R8//Wp7F1u7xhriuxaRiZoFEPaCIsrvK4= ------END CERTIFICATE REQUEST----- diff --git a/test/resources/privatekey.pem b/test/resources/privatekey.pem deleted file mode 100644 index 3a39a2392..000000000 --- a/test/resources/privatekey.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQC4sXy8K7ww2AGmlOtqMnM/M8FKcQoLssl8oVQzfyjJj/yPh35G -3CK6o7VtI26UVoi4Z+XMOCv3FE5HzI+AMGjebds6tBVRSXSHXS8c7mpsiSyRWX7O -bD2FrV+pqKz3LvUTO48G90dVfOdl41VlI/J3lALu3lMyqEkOe+ZB7Jyq3QIDAQAB -AoGAYCTkzf/mY3bOxSzYr9u7ardCc8IMfLKBeMNy1avoS6UM0Jqz/acy3P3DwCCl -u8qgOX68fWbwXBrR9UZjnVOWAvAgACS9bSTR4UxXuHve9YHf1s1Idm1Ck8CopiuY -0PTiuF7OJp6U7fc1RjO5F5tvSMuYbh+68Vpx9SQRfDHYqnECQQD1KnhSRDjLCfoB -lLfTew99W51OTx2NPRKRXwZH/YwlgRl/cAgJhdemah/AAavB6BUdqEXdiIviEHuT -UsfAXhf7AkEAwNrmEI3B4gtMRKJAsyWAKGFxDHuC9wGkhSxCVihQuxXtqEMX7Qnx -ucU9bRRtUgVPcOmFEtpPsI4e0wkTMg+ZBwJAPL+ERuYuqGjVcPTXw+g3Q1mjFddW -vDuI0UqZdNcnlddyaPhqlWl7sPmU2m/PjmGicdHTVfxSpPZumGenpUvrZwJAdodS -9QObEOmus1Qhfbljne3dhDV5FYTd77d3Aer/Syy8BzlNQDNnbKysBxmR4uI+o//x -+NdSOQnwKfYe5RqvCwJBAMfq911uzlD8Kd9s0n+MJe8b5/duYOtgPZvaIFWOWyNm -0aJE/VovVhk2JGvIU9kxdgt9O4N0x2XukS2hq7I1Xts= ------END RSA PRIVATE KEY----- diff --git a/yarn.lock b/yarn.lock index 1493a2135..57ecb6376 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,51 +2,10 @@ # yarn lockfile v1 -"@types/express-serve-static-core@*": - version "4.0.45" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.45.tgz#71bb1f87d7187482d0d8851f5b294458e1c78667" - dependencies: - "@types/node" "*" - -"@types/express@~4.0.35": - version "4.0.35" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.35.tgz#6267c7b60a51fac473467b3c4a02cd1e441805fe" - dependencies: - "@types/express-serve-static-core" "*" - "@types/serve-static" "*" - -"@types/mime@*": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-0.0.29.tgz#fbcfd330573b912ef59eeee14602bface630754b" - -"@types/node@*": - version "7.0.29" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.29.tgz#ccfcec5b7135c7caf6c4ffb8c7f33102340d99df" - -"@types/serve-static@*": - version "1.7.31" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.31.tgz#15456de8d98d6b4cff31be6c6af7492ae63f521a" - dependencies: - "@types/express-serve-static-core" "*" - "@types/mime" "*" - -"@types/socket.io@~1.4.27": - version "1.4.29" - resolved "https://registry.yarnpkg.com/@types/socket.io/-/socket.io-1.4.29.tgz#86a6b3a9ab78cf9a900ceef85b9b68b6bea86712" - dependencies: - "@types/node" "*" - abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accepts@1.3.3, accepts@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" - dependencies: - mime-types "~2.1.11" - negotiator "0.6.1" - acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" @@ -61,10 +20,6 @@ acorn@^5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" - agent-base@2: version "2.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7" @@ -147,10 +102,6 @@ arr-flatten@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -172,10 +123,6 @@ array.prototype.find@^2.0.1: define-properties "^1.1.2" es-abstract "^1.7.0" -arraybuffer.slice@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" - arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -556,7 +503,7 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" -babel-polyfill@^6.20.0, babel-polyfill@^6.23.0: +babel-polyfill@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" dependencies: @@ -649,63 +596,26 @@ babylon@^6.13.0, babylon@^6.17.2: version "6.17.3" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.3.tgz#1327d709950b558f204e5352587fd0290f8d8e48" -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - balanced-match@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" -base64-arraybuffer@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" - -base64id@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" - bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" dependencies: tweetnacl "^0.14.3" -better-assert@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" - dependencies: - callsite "1.0.0" - binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" -blob@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" - block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" dependencies: inherits "~2.0.0" -body-parser@^1.15.2: - version "1.17.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee" - dependencies: - bytes "2.4.0" - content-type "~1.0.2" - debug "2.6.7" - depd "~1.1.0" - http-errors "~1.6.1" - iconv-lite "0.4.15" - on-finished "~2.3.0" - qs "6.4.0" - raw-body "~2.2.0" - type-is "~1.6.15" - boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -731,20 +641,12 @@ browser-stdout@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" -bytes@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" - caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" dependencies: callsites "^0.2.0" -callsite@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" - callsites@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" @@ -852,18 +754,6 @@ commander@2.9.0, commander@^2.8.1: dependencies: graceful-readlink ">= 1.0.0" -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" - -component-emitter@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -886,26 +776,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - -content-type@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" - convert-source-map@^1.1.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - core-js@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" @@ -944,30 +818,18 @@ debug-log@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" -debug@2, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.0, debug@^2.6.3, debug@~2.6.4, debug@~2.6.6: +debug@2, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.0, debug@^2.6.3: version "2.6.8" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: ms "2.0.0" -debug@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" - dependencies: - ms "0.7.2" - debug@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b" dependencies: ms "0.7.2" -debug@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" - dependencies: - ms "2.0.0" - decamelize@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -1024,14 +886,6 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -depd@1.1.0, depd@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -1083,54 +937,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - -encodeurl@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" - -engine.io-client@~3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.1.tgz#415a9852badb14fa008fa3ef1e31608db6761325" - dependencies: - component-emitter "1.2.1" - component-inherit "0.0.3" - debug "~2.6.4" - engine.io-parser "~2.1.1" - has-cors "1.1.0" - indexof "0.0.1" - parsejson "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - ws "~2.3.1" - xmlhttprequest-ssl "1.5.3" - yeast "0.1.2" - -engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.1.tgz#e0fb3f0e0462f7f58bb77c1a52e9f5a7e26e4668" - dependencies: - after "0.8.2" - arraybuffer.slice "0.0.6" - base64-arraybuffer "0.1.5" - blob "0.0.4" - has-binary2 "~1.0.2" - -engine.io@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.0.tgz#5ca438e3ce9fdbc915c4a21c8dd9e1266706e57e" - dependencies: - accepts "1.3.3" - base64id "1.0.0" - cookie "0.3.1" - debug "~2.6.4" - engine.io-parser "~2.1.0" - ws "~2.3.1" - optionalDependencies: - uws "~0.14.4" - entities@1.0: version "1.0.0" resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" @@ -1214,10 +1020,6 @@ es6-weak-map@^2.0.1: es6-iterator "^2.0.1" es6-symbol "^3.1.1" -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -1332,10 +1134,6 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" -etag@~1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" - event-emitter@~0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -1367,39 +1165,6 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -express@^4.14.0: - version "4.15.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.15.3.tgz#bab65d0f03aa80c358408972fc700f916944b662" - dependencies: - accepts "~1.3.3" - array-flatten "1.1.1" - content-disposition "0.5.2" - content-type "~1.0.2" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.7" - depd "~1.1.0" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.0" - finalhandler "~1.0.3" - fresh "0.5.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.1" - path-to-regexp "0.1.7" - proxy-addr "~1.1.4" - qs "6.4.0" - range-parser "~1.2.0" - send "0.15.3" - serve-static "1.12.3" - setprototypeof "1.0.3" - statuses "~1.3.1" - type-is "~1.6.15" - utils-merge "1.0.0" - vary "~1.1.1" - extend@3, extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -1418,43 +1183,10 @@ fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" -feathers-commons@^0.8.0, feathers-commons@^0.8.7: +feathers-commons@^0.8.7: version "0.8.7" resolved "https://registry.yarnpkg.com/feathers-commons/-/feathers-commons-0.8.7.tgz#11c6f25b537745a983e8d61552d7db8932d53782" -feathers-errors@^2.0.1, feathers-errors@^2.2.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/feathers-errors/-/feathers-errors-2.8.1.tgz#98fc85ad4f48c12d25eca3c2556beea2c0394b8b" - dependencies: - debug "^2.2.0" - -feathers-rest@^1.5.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/feathers-rest/-/feathers-rest-1.7.3.tgz#4d307371f1c959527e40f159cf9369a14ba28a25" - dependencies: - debug "^2.2.0" - feathers-commons "^0.8.0" - feathers-errors "^2.0.1" - qs "^6.4.0" - -feathers-socket-commons@^2.0.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/feathers-socket-commons/-/feathers-socket-commons-2.4.0.tgz#062efd57f9a8716644145b993a5f72709969f1e1" - dependencies: - debug "^2.2.0" - feathers-commons "^0.8.0" - feathers-errors "^2.2.0" - -feathers-socketio@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/feathers-socketio/-/feathers-socketio-2.0.0.tgz#ce62e33627deaad53279e4f9b67cdac0f2cf49de" - dependencies: - "@types/socket.io" "~1.4.27" - debug "^2.2.0" - feathers-socket-commons "^2.0.0" - socket.io "^2.0.1" - uberproto "^1.2.0" - figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -1490,18 +1222,6 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89" - dependencies: - debug "2.6.7" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.1" - statuses "~1.3.1" - unpipe "~1.0.0" - find-root@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.0.0.tgz#962ff211aab25c6520feeeb8d6287f8f6e95807a" @@ -1547,14 +1267,6 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" - -fresh@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" - fs-readdir-recursive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" @@ -1713,16 +1425,6 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-binary2@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98" - dependencies: - isarray "2.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" @@ -1767,15 +1469,6 @@ htmlparser2@3.8.x: entities "1.0" readable-stream "1.1" -http-errors@~1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" - dependencies: - depd "1.1.0" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -1792,10 +1485,6 @@ https-proxy-agent@^1.0.0: debug "2" extend "3" -iconv-lite@0.4.15: - version "0.4.15" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" - ignore@^3.0.9, ignore@^3.2.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" @@ -1804,10 +1493,6 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -1815,7 +1500,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -1851,10 +1536,6 @@ invariant@^2.2.0: dependencies: loose-envify "^1.0.0" -ipaddr.js@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -1998,10 +1679,6 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" - isemail@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/isemail/-/isemail-1.2.0.tgz#be03df8cc3e29de4d2c5df6501263f1fa4595e9a" @@ -2288,18 +1965,6 @@ loose-envify@^1.0.0: dependencies: js-tokens "^3.0.0" -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -2322,16 +1987,12 @@ mime-db@~1.27.0: version "1.27.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: +mime-types@^2.1.12, mime-types@~2.1.7: version "2.1.15" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" dependencies: mime-db "~1.27.0" -mime@1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" - minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2392,10 +2053,6 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - node-pre-gyp@^0.6.29: version "0.6.36" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" @@ -2466,14 +2123,10 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@~4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" -object-component@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" - object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" @@ -2485,12 +2138,6 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - once@^1.3.0, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -2567,28 +2214,6 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parsejson@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" - dependencies: - better-assert "~1.0.0" - -parseqs@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" - dependencies: - better-assert "~1.0.0" - -parseuri@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" - dependencies: - better-assert "~1.0.0" - -parseurl@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -2605,10 +2230,6 @@ path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" @@ -2666,22 +2287,11 @@ progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" -proxy-addr@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" - dependencies: - forwarded "~0.1.0" - ipaddr.js "1.3.0" - punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" -q@^1.4.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" - -qs@6.4.0, qs@^6.4.0, qs@~6.4.0: +qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" @@ -2692,18 +2302,6 @@ randomatic@^1.1.3: is-number "^3.0.0" kind-of "^4.0.0" -range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -raw-body@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96" - dependencies: - bytes "2.4.0" - iconv-lite "0.4.15" - unpipe "1.0.0" - rc@^1.1.2, rc@^1.1.7: version "1.2.1" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" @@ -2816,7 +2414,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.81.0, request@^2.x: +request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -2879,10 +2477,6 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1: dependencies: glob "^7.0.5" -rubberduck@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/rubberduck/-/rubberduck-1.1.1.tgz#cd2cda4b867178135eafc995a71384f5f743db02" - run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -2897,11 +2491,7 @@ rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" -safe-buffer@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.0.tgz#fe4c8460397f9eaaaa58e73be46273408a45e223" - -safe-buffer@~5.0.1: +safe-buffer@^5.0.1, safe-buffer@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" @@ -2926,33 +2516,6 @@ semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" -send@0.15.3: - version "0.15.3" - resolved "https://registry.yarnpkg.com/send/-/send-0.15.3.tgz#5013f9f99023df50d1bd9892c19e3defd1d53309" - dependencies: - debug "2.6.7" - depd "~1.1.0" - destroy "~1.0.4" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.0" - fresh "0.5.0" - http-errors "~1.6.1" - mime "1.3.4" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.3.1" - -serve-static@1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.3.tgz#9f4ba19e2f3030c547f8af99107838ec38d5b1e2" - dependencies: - encodeurl "~1.0.1" - escape-html "~1.0.3" - parseurl "~1.3.1" - send "0.15.3" - set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -2961,10 +2524,6 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - shelljs@0.3.x: version "0.3.0" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.3.0.tgz#3596e6307a781544f591f37da618360f31db57b1" @@ -2995,50 +2554,6 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -socket.io-adapter@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.0.tgz#c7aa46501dd556c2cb8a28af8ff95c0b5e1daa4c" - dependencies: - debug "2.3.3" - -socket.io-client@^2.0.0, socket.io-client@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.2.tgz#86f59db59ba1168724220f39be281ad49af742c1" - dependencies: - backo2 "1.0.2" - base64-arraybuffer "0.1.5" - component-bind "1.0.0" - component-emitter "1.2.1" - debug "~2.6.4" - engine.io-client "~3.1.0" - has-cors "1.1.0" - indexof "0.0.1" - object-component "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - socket.io-parser "~3.1.1" - to-array "0.1.4" - -socket.io-parser@~3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.2.tgz#dbc2282151fc4faebbe40aeedc0772eba619f7f2" - dependencies: - component-emitter "1.2.1" - debug "~2.6.4" - has-binary2 "~1.0.2" - isarray "2.0.1" - -socket.io@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.2.tgz#133bf3a1b67d02f2ac65103c11f78e6f2c4f3b3a" - dependencies: - debug "~2.6.6" - engine.io "~3.1.0" - object-assign "~4.1.1" - socket.io-adapter "~1.1.0" - socket.io-client "~2.0.2" - socket.io-parser "~3.1.1" - source-map-support@^0.4.2: version "0.4.15" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" @@ -3083,10 +2598,6 @@ standard-engine@~5.4.0: minimist "^1.1.0" pkg-conf "^2.0.0" -"statuses@>= 1.3.1 < 2", statuses@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" - string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -3199,10 +2710,6 @@ through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" - to-fast-properties@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" @@ -3243,13 +2750,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-is@~1.6.15: - version "1.6.15" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" - dependencies: - media-typer "0.3.0" - mime-types "~2.1.15" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -3275,18 +2775,10 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" -ultron@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" - uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - user-home@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" @@ -3301,28 +2793,16 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" - uuid@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" -uws@~0.14.4: - version "0.14.5" - resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc" - v8flags@^2.0.10: version "2.1.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" dependencies: user-home "^1.1.1" -vary@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" - verror@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" @@ -3374,17 +2854,6 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-2.3.1.tgz#6b94b3e447cb6a363f785eaf94af6359e8e81c80" - dependencies: - safe-buffer "~5.0.1" - ultron "~1.1.0" - -xmlhttprequest-ssl@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" - xtend@^4.0.0, xtend@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -3397,7 +2866,3 @@ yargs@~3.10.0: cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" - -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"