diff --git a/.github/issue_template.md b/.github/issue_template.md index 57a8f3e0f1..249464ce8b 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -26,4 +26,4 @@ Tell us about the applicable parts of your setup. **React Native Version**: -**Module Loader**: \ No newline at end of file +**Module Loader**: diff --git a/.gitignore b/.gitignore index 92ae34318d..03ebbc208a 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,6 @@ packages/authentication-client/lib packages/authentication-oauth/lib packages/configuration/lib packages/commons/lib +packages/errors/lib packages/tests/lib packages/transport-commons/lib diff --git a/package-lock.json b/package-lock.json index 86b90890ae..87f145175e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1290,9 +1290,9 @@ } }, "semver": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.2.tgz", - "integrity": "sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "which": { @@ -1493,9 +1493,9 @@ } }, "@octokit/types": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.8.2.tgz", - "integrity": "sha512-8cs4DjRAzFoGo1ieUhDyrTdPK016V3JD/H00nbnojSCUYfOXnnJ1owUaAT/3OebTzp/tgdTG6M+8PdCTJzI+/w==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.9.0.tgz", + "integrity": "sha512-IzptUpoDsFlXF+AOys+KnfItIVY3EK+eH9Akv+lJYELnMSGkJnIcClt6Cm0QRR4ecsUTsmFJWn10iFgJ9BQqIQ==", "dev": true, "requires": { "@types/node": ">= 8" @@ -2400,9 +2400,9 @@ } }, "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", "dev": true }, "cliui": { @@ -6232,9 +6232,9 @@ "dev": true }, "semver": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.2.tgz", - "integrity": "sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "supports-color": { @@ -6258,9 +6258,9 @@ }, "dependencies": { "semver": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.2.tgz", - "integrity": "sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true } } @@ -6521,9 +6521,9 @@ } }, "semver": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.2.tgz", - "integrity": "sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "socks-proxy-agent": { @@ -7211,9 +7211,9 @@ } }, "semver": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.2.2.tgz", - "integrity": "sha512-Zo84u6o2PebMSK3zjJ6Zp5wi8VnQZnEaCP13Ul/lt1ANsLACxnJxq4EEm1PY94/por1Hm9+7xpIswdS5AkieMA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", "dev": true }, "ssri": { @@ -7900,9 +7900,9 @@ } }, "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz", + "integrity": "sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==", "dev": true, "requires": { "path-parse": "^1.0.6" @@ -8891,23 +8891,13 @@ } }, "uglify-js": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.8.1.tgz", - "integrity": "sha512-W7KxyzeaQmZvUFbGj4+YFshhVrMBGSg2IbcYAjGWGvx8DHvJMclbTDMpffdxFUGPBHjIytk7KJUR/KUXstUGDw==", + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.1.tgz", + "integrity": "sha512-JUPoL1jHsc9fOjVFHdQIhqEEJsQvfKDjlubcCilu8U26uZ73qOg8VsN8O1jbuei44ZPlwL7kmbAdM4tzaUvqnA==", "dev": true, "optional": true, "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } + "commander": "~2.20.3" } }, "uid-number": { diff --git a/packages/authentication-client/package.json b/packages/authentication-client/package.json index c0a8313502..aacfaa69bf 100644 --- a/packages/authentication-client/package.json +++ b/packages/authentication-client/package.json @@ -52,8 +52,6 @@ "devDependencies": { "@feathersjs/authentication-local": "^4.5.2", "@feathersjs/express": "^4.5.2", - "@feathersjs/primus": "^4.5.2", - "@feathersjs/primus-client": "^4.5.2", "@feathersjs/rest-client": "^4.5.2", "@feathersjs/socketio": "^4.5.2", "@feathersjs/socketio-client": "^4.5.2", diff --git a/packages/authentication-client/src/core.ts b/packages/authentication-client/src/core.ts index 77178872f4..fcba078c69 100644 --- a/packages/authentication-client/src/core.ts +++ b/packages/authentication-client/src/core.ts @@ -37,7 +37,7 @@ export class AuthenticationClient { options: AuthenticationClientOptions; constructor (app: Application, options: AuthenticationClientOptions) { - const socket = app.io || app.primus; + const socket = app.io || (app as any).primus; const storage = new StorageWrapper(app.get('storage') || options.storage); this.app = app; diff --git a/packages/authentication-client/src/index.ts b/packages/authentication-client/src/index.ts index d2f7d49354..ea1b2ad808 100644 --- a/packages/authentication-client/src/index.ts +++ b/packages/authentication-client/src/index.ts @@ -7,7 +7,6 @@ declare module '@feathersjs/feathers' { interface Application { io?: any; rest?: any; - primus?: any; authentication: AuthenticationClient; authenticate: AuthenticationClient['authenticate']; reAuthenticate: AuthenticationClient['reAuthenticate']; diff --git a/packages/authentication-client/test/integration/primus.test.ts b/packages/authentication-client/test/integration/primus.test.ts deleted file mode 100644 index f08f65f152..0000000000 --- a/packages/authentication-client/test/integration/primus.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -// @ts-ignore -import Primus from 'primus'; -// @ts-ignore -import Emitter from 'primus-emitter'; -import feathers, { Application } from '@feathersjs/feathers'; -import primusClient from '@feathersjs/primus-client'; -import primus from '@feathersjs/primus'; - -import authClient from '../../src'; -import getApp from './fixture'; -import commonTests from './commons'; - -const port = 8998; -const baseURL = `http://localhost:${port}`; -const Socket = Primus.createSocket({ - transformer: 'websockets', - plugin: { - emitter: Emitter - } -}); - -describe('@feathersjs/authentication-client Primus integration', () => { - let app: Application; - let server: any; - - before(() => { - app = getApp(feathers().configure(primus({ - transformer: 'websockets' - }))); - - server = app.listen(port); - }); - - after(() => server.close()); - - commonTests(() => app, () => { - return feathers() - .configure(primusClient(new Socket(baseURL), { timeout: 1000 })) - .configure(authClient()); - }, { - email: 'primusauth@feathersjs.com', - password: 'secrets', - provider: 'primus' - }); -}); diff --git a/packages/client/index.d.ts b/packages/client/index.d.ts index 70dff7ed41..38466f1f0d 100644 --- a/packages/client/index.d.ts +++ b/packages/client/index.d.ts @@ -1,7 +1,6 @@ import feathers from '@feathersjs/feathers'; import authentication from '@feathersjs/authentication-client'; -import errors from '@feathersjs/errors'; -import primus from '@feathersjs/primus-client'; +import * as errors from '@feathersjs/errors'; import rest from '@feathersjs/rest-client'; import socketio from '@feathersjs/socketio-client'; @@ -13,14 +12,12 @@ export = feathersClient; type Feathers = typeof feathers; type FeathersAuthenticationClient = typeof authentication; type FeathersErrors = typeof errors; -type FeathersPrimusClient = typeof primus; type FeathersRestClient = typeof rest; type FeathersSocketIOClient = typeof socketio; interface FeathersClient extends Feathers { authentication: FeathersAuthenticationClient; errors: FeathersErrors; - primus: FeathersPrimusClient; rest: FeathersRestClient; socketio: FeathersSocketIOClient; } diff --git a/packages/client/package.json b/packages/client/package.json index 5791fa53d1..8d9cc785ff 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@feathersjs/client", - "description": "A module that consolidates Feathers client modules for REST (jQuery, Request, Superagent) and Websocket (Socket.io, Primus) connections", + "description": "A module that consolidates Feathers client modules for REST and Websocket connections", "version": "4.5.2", "repository": { "type": "git", @@ -38,18 +38,18 @@ "last 2 versions", "IE 10" ], - "devDependencies": { - "@babel/core": "^7.9.0", - "@babel/preset-env": "^7.9.5", + "dependencies": { "@feathersjs/authentication-client": "^4.5.2", "@feathersjs/errors": "^4.5.2", "@feathersjs/express": "^4.5.2", "@feathersjs/feathers": "^4.5.2", - "@feathersjs/primus": "^4.5.2", - "@feathersjs/primus-client": "^4.5.2", "@feathersjs/rest-client": "^4.5.2", "@feathersjs/socketio": "^4.5.2", - "@feathersjs/socketio-client": "^4.5.2", + "@feathersjs/socketio-client": "^4.5.2" + }, + "devDependencies": { + "@babel/core": "^7.9.0", + "@babel/preset-env": "^7.9.0", "@feathersjs/tests": "^4.5.2", "babel-loader": "^8.1.0", "body-parser": "^1.19.0", @@ -60,7 +60,6 @@ "mocha-puppeteer": "^0.14.0", "node-fetch": "^2.6.0", "parallel-webpack": "^2.6.0", - "request": "^2.88.2", "socket.io-client": "^2.3.0", "superagent": "^5.2.2", "uglifyjs-webpack-plugin": "^2.2.0", diff --git a/packages/client/primus.js b/packages/client/primus.js deleted file mode 100644 index 53970a2f9c..0000000000 --- a/packages/client/primus.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./dist/primus'); diff --git a/packages/client/src/index.js b/packages/client/src/index.js index 806ad42dbd..2ec1dd457c 100644 --- a/packages/client/src/index.js +++ b/packages/client/src/index.js @@ -3,12 +3,10 @@ const errors = require('@feathersjs/errors'); const authentication = require('@feathersjs/authentication-client'); const rest = require('@feathersjs/rest-client'); const socketio = require('@feathersjs/socketio-client'); -const primus = require('@feathersjs/primus-client'); Object.assign(feathers, { errors, socketio, - primus, rest, authentication }); diff --git a/packages/client/src/primus.js b/packages/client/src/primus.js deleted file mode 100644 index 0def5eb195..0000000000 --- a/packages/client/src/primus.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@feathersjs/primus-client'); diff --git a/packages/client/test/rest/request.test.js b/packages/client/test/rest/request.test.js deleted file mode 100644 index b02e52bd0c..0000000000 --- a/packages/client/test/rest/request.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const request = require('request'); -const baseTests = require('@feathersjs/tests/lib/client'); - -const app = require('../fixture'); -const feathers = require('../../'); - -describe('node-request REST connector', function () { - const rest = feathers.rest('http://localhost:6777'); - const client = feathers() - .configure(rest.request(request)); - - before(function (done) { - this.server = app().listen(6777, done); - }); - - after(function (done) { - this.server.close(done); - }); - - baseTests(client, 'todos'); -}); diff --git a/packages/client/test/sockets/primus.test.js b/packages/client/test/sockets/primus.test.js deleted file mode 100644 index 38c00275de..0000000000 --- a/packages/client/test/sockets/primus.test.js +++ /dev/null @@ -1,29 +0,0 @@ -const primus = require('@feathersjs/primus'); -const baseTests = require('@feathersjs/tests/lib/client'); - -const app = require('../fixture'); -const feathers = require('../../'); - -describe('Primus connector', function () { - const client = feathers(); - - let socket; - - before(function (done) { - this.server = app(function () { - this.configure(primus({ - transformer: 'websockets' - }, function (primus) { - socket = new primus.Socket('http://localhost:12012'); - client.configure(feathers.primus(socket)); - })); - }).listen(12012, done); - }); - - after(function () { - socket.socket.close(); - this.server.close(); - }); - - baseTests(client, 'todos'); -}); diff --git a/packages/client/test/sockets/socketio.test.js b/packages/client/test/sockets/socketio.test.js index 31ad9bff94..b6840767ad 100644 --- a/packages/client/test/sockets/socketio.test.js +++ b/packages/client/test/sockets/socketio.test.js @@ -3,7 +3,7 @@ const socketio = require('@feathersjs/socketio'); const baseTests = require('@feathersjs/tests/lib/client'); const app = require('../fixture'); -const feathers = require('../../'); +const feathers = require('../../src'); describe('Socket.io connector', function () { const socket = io('http://localhost:9988'); diff --git a/packages/client/webpack.config.js b/packages/client/webpack.config.js index 137839964f..c11813ad10 100644 --- a/packages/client/webpack.config.js +++ b/packages/client/webpack.config.js @@ -6,7 +6,10 @@ const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); function createConfig (name, isProduction = false) { const output = name === 'index' ? 'feathers' : name; const commons = { - entry: `./src/${name}.js`, + entry: [ + 'regenerator-runtime/runtime', + `./src/${name}.js` + ], output: { library: 'feathers', libraryTarget: 'umd', @@ -61,8 +64,6 @@ module.exports = [ createConfig('rest', true), createConfig('socketio'), createConfig('socketio', true), - createConfig('primus'), - createConfig('primus', true), createConfig('authentication'), createConfig('authentication', true) ]; diff --git a/packages/commons/package.json b/packages/commons/package.json index 379f59ad55..644ff33a3d 100644 --- a/packages/commons/package.json +++ b/packages/commons/package.json @@ -40,6 +40,9 @@ "publishConfig": { "access": "public" }, + "dependencies": { + "@feathersjs/hooks": "^0.4.0-alpha.0" + }, "devDependencies": { "@types/mocha": "^7.0.2", "@types/node": "^13.11.1", diff --git a/packages/commons/src/hooks.ts b/packages/commons/src/hooks.ts index 9852c01422..7fa9314999 100644 --- a/packages/commons/src/hooks.ts +++ b/packages/commons/src/hooks.ts @@ -1,6 +1,8 @@ +import { HookContext } from '@feathersjs/hooks'; import { createSymbol, _ } from './utils'; -const { each, pick } = _; +const { each, pick, omit } = _; +const noop = () => {}; export const ACTIVATE_HOOKS = createSymbol('__feathersActivateHooks'); @@ -87,9 +89,13 @@ export function convertHookData (obj: any) { // Duck-checks a given object to be a hook object // A valid hook object has `type` and `method` export function isHookObject (hookObject: any) { - return typeof hookObject === 'object' && - typeof hookObject.method === 'string' && - typeof hookObject.type === 'string'; + return ( + hookObject instanceof HookContext || ( + typeof hookObject === 'object' && + typeof hookObject.method === 'string' && + typeof hookObject.type === 'string' + ) + ); } // Returns all service and application hooks combined @@ -193,3 +199,132 @@ export function enableHooks (obj: any, methods: string[], types: string[]) { } }); } + +async function handleError (hook: any, context: any, onError: any) { + try { + const result = await hook.call(context.self, context); + Object.assign(context, omit(result, 'arguments')); + } catch (errorError) { + if (typeof onError === 'function') { + onError(errorError, context); + } + throw errorError; + } + + if (typeof context.error !== 'undefined') { + throw context.error; + } +} + +export function firstHook (context: any, next: any) { + context.type = 'before'; + return next(); +} + +export function lastHook (context: any, next: any) { + context.type = 'after'; + return next(); +} + +export function toBeforeHook (hook: any) { + return async (context: any, next: any) => { + const result = await hook.call(context.self, context); + Object.assign(context, omit(result, 'arguments')); + await next(); + }; +} + +export function toAfterHook (hook: any) { + return async (context: any, next: any) => { + await next(); + const result = await hook.call(context.self, context); + Object.assign(context, omit(result, 'arguments')); + }; +} + +export function toErrorHook (hook: any, onError: any, control: any) { + return async (context: any, next: any) => { + try { + await next(); + } catch (error) { + if (typeof control === 'function') { + control(context); + } + + context.error = error; + context.result = undefined; + + await handleError(hook, context, onError); + } + }; +} + +export function toFinallyHook (hook: any, onError: any, control: any) { + return async (context: any, next: any) => { + try { + await next(); + } catch (error) { + throw error; + } finally { + if (typeof control === 'function') { + control(context); + } + + await handleError(hook, context, onError); + } + }; +} + +export function beforeWrapper (hooks: any) { + return [firstHook, ...[].concat(hooks).map(toBeforeHook)]; +} + +export function afterWrapper (hooks: any) { + return [...[].concat(hooks).reverse().map(toAfterHook), lastHook]; +} + +export function finallyWrapper (hooks: any) { + let errorInFinally: any; + + const onError = (error: any, context: any) => { + errorInFinally = error; + context.error = error; + context.result = undefined; + }; + const control = () => { + if (errorInFinally) { + throw errorInFinally; + } + }; + + return [].concat(hooks).reverse().map(hook => toFinallyHook(hook, onError, control)); +} + +export function errorWrapper (hooks: any) { + let errorInError: any; + + const onError = (error: any, context: any) => { + errorInError = error; + context.error = error; + context.result = undefined; + }; + const control = (context: any) => { + if (!context.original) { + context.original = { ...context }; + } + if (errorInError) { + throw errorInError; + } + context.type = 'error'; + }; + + return [noop].concat(hooks).reverse().map(hook => toErrorHook(hook, onError, control)); +} + +export function wrap ({ async = [], before = [], after = [] }: any = {}) { + return [ + ...[].concat(async), + ...beforeWrapper(before), + ...afterWrapper(after) + ]; +} diff --git a/packages/errors/.npmignore b/packages/errors/.npmignore index 65e3ba2eda..d423d51243 100644 --- a/packages/errors/.npmignore +++ b/packages/errors/.npmignore @@ -1 +1,2 @@ test/ +tsconfig.json diff --git a/packages/errors/index.d.ts b/packages/errors/index.d.ts deleted file mode 100644 index ae1dff39e9..0000000000 --- a/packages/errors/index.d.ts +++ /dev/null @@ -1,122 +0,0 @@ -export interface FeathersErrorJSON { - readonly name: string; - readonly message: string; - readonly code: number; - readonly className: string; - readonly data: any; - readonly errors: any; -} - -export class FeathersError extends Error { - readonly code: number; - readonly className: string; - readonly data: any; - readonly errors: any; - constructor (msg: string | Error, name: string, code: number, className: string, data: any); - toJSON (): FeathersErrorJSON; -} - -export class BadRequest extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class NotAuthenticated extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class PaymentError extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class Forbidden extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class NotFound extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class MethodNotAllowed extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class NotAcceptable extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class Timeout extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class Conflict extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class LengthRequired extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class Unprocessable extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class TooManyRequests extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class GeneralError extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class NotImplemented extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class BadGateway extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export class Unavailable extends FeathersError { - constructor (msg?: string | Error, data?: any); -} - -export interface Errors { - FeathersError: FeathersError; - BadRequest: BadRequest; - NotAuthenticated: NotAuthenticated; - PaymentError: PaymentError; - Forbidden: Forbidden; - NotFound: NotFound; - MethodNotAllowed: MethodNotAllowed; - NotAcceptable: NotAcceptable; - Timeout: Timeout; - Conflict: Conflict; - LengthRequired: LengthRequired; - Unprocessable: Unprocessable; - TooManyRequests: TooManyRequests; - GeneralError: GeneralError; - NotImplemented: NotImplemented; - BadGateway: BadGateway; - Unavailable: Unavailable; - 400: BadRequest; - 401: NotAuthenticated; - 402: PaymentError; - 403: Forbidden; - 404: NotFound; - 405: MethodNotAllowed; - 406: NotAcceptable; - 408: Timeout; - 409: Conflict; - 411: LengthRequired; - 422: Unprocessable; - 429: TooManyRequests; - 500: GeneralError; - 501: NotImplemented; - 502: BadGateway; - 503: Unavailable; -} - -export function convert (error: any): FeathersError; - -export const types: Errors; -export const errors: Errors; diff --git a/packages/errors/lib/index.js b/packages/errors/lib/index.js deleted file mode 100644 index b1f5ca7127..0000000000 --- a/packages/errors/lib/index.js +++ /dev/null @@ -1,258 +0,0 @@ -const debug = require('debug')('@feathersjs/errors'); - -function FeathersError (msg, name, code, className, data) { - msg = msg || 'Error'; - - let errors; - let message; - let newData; - - if (msg instanceof Error) { - message = msg.message || 'Error'; - - // NOTE (EK): This is typically to handle validation errors - if (msg.errors) { - errors = msg.errors; - } - } else if (typeof msg === 'object') { // Support plain old objects - message = msg.message || 'Error'; - data = msg; - } else { // message is just a string - message = msg; - } - - if (data) { - // NOTE(EK): To make sure that we are not messing - // with immutable data, just make a copy. - // https://github.com/feathersjs/errors/issues/19 - newData = JSON.parse(JSON.stringify(data)); - - if (newData.errors) { - errors = newData.errors; - delete newData.errors; - } else if (data.errors) { - // The errors property from data could be - // stripped away while cloning resulting newData not to have it - // For example: when cloning arrays this property - errors = JSON.parse(JSON.stringify(data.errors)); - } - } - - // NOTE (EK): Babel doesn't support this so - // we have to pass in the class name manually. - // this.name = this.constructor.name; - this.type = 'FeathersError'; - this.name = name; - this.message = message; - this.code = code; - this.className = className; - this.data = newData; - this.errors = errors || {}; - - debug(`${this.name}(${this.code}): ${this.message}`); - debug(this.errors); - - if (Error.captureStackTrace) { - Error.captureStackTrace(this, FeathersError); - } else { - this.stack = (new Error()).stack; - } -} - -function inheritsFrom (Child, Parent) { - Child.prototype = Object.create(Parent.prototype); - Child.prototype.constructor = Child; -} - -inheritsFrom(FeathersError, Error); - -// NOTE (EK): A little hack to get around `message` not -// being included in the default toJSON call. -Object.defineProperty(FeathersError.prototype, 'toJSON', { - value: function () { - return { - name: this.name, - message: this.message, - code: this.code, - className: this.className, - data: this.data, - errors: this.errors - }; - } -}); - -// 400 - Bad Request -function BadRequest (message, data) { - FeathersError.call(this, message, 'BadRequest', 400, 'bad-request', data); -} - -inheritsFrom(BadRequest, FeathersError); - -// 401 - Not Authenticated -function NotAuthenticated (message, data) { - FeathersError.call(this, message, 'NotAuthenticated', 401, 'not-authenticated', data); -} - -inheritsFrom(NotAuthenticated, FeathersError); - -// 402 - Payment Error -function PaymentError (message, data) { - FeathersError.call(this, message, 'PaymentError', 402, 'payment-error', data); -} - -inheritsFrom(PaymentError, FeathersError); - -// 403 - Forbidden -function Forbidden (message, data) { - FeathersError.call(this, message, 'Forbidden', 403, 'forbidden', data); -} - -inheritsFrom(Forbidden, FeathersError); - -// 404 - Not Found -function NotFound (message, data) { - FeathersError.call(this, message, 'NotFound', 404, 'not-found', data); -} - -inheritsFrom(NotFound, FeathersError); - -// 405 - Method Not Allowed -function MethodNotAllowed (message, data) { - FeathersError.call(this, message, 'MethodNotAllowed', 405, 'method-not-allowed', data); -} - -inheritsFrom(MethodNotAllowed, FeathersError); - -// 406 - Not Acceptable -function NotAcceptable (message, data) { - FeathersError.call(this, message, 'NotAcceptable', 406, 'not-acceptable', data); -} - -inheritsFrom(NotAcceptable, FeathersError); - -// 408 - Timeout -function Timeout (message, data) { - FeathersError.call(this, message, 'Timeout', 408, 'timeout', data); -} - -inheritsFrom(Timeout, FeathersError); - -// 409 - Conflict -function Conflict (message, data) { - FeathersError.call(this, message, 'Conflict', 409, 'conflict', data); -} - -inheritsFrom(Conflict, FeathersError); - -// 410 - Gone -function Gone (message, data) { - FeathersError(this, message, 'Gone', 410, 'gone', data); -} - -inheritsFrom(Gone, FeathersError); - -// 411 - Length Required -function LengthRequired (message, data) { - FeathersError.call(this, message, 'LengthRequired', 411, 'length-required', data); -} - -inheritsFrom(LengthRequired, FeathersError); - -// 422 Unprocessable -function Unprocessable (message, data) { - FeathersError.call(this, message, 'Unprocessable', 422, 'unprocessable', data); -} - -inheritsFrom(Unprocessable, FeathersError); - -// 429 Too Many Requests -function TooManyRequests (message, data) { - FeathersError.call(this, message, 'TooManyRequests', 429, 'too-many-requests', data); -} - -inheritsFrom(TooManyRequests, FeathersError); - -// 500 - General Error -function GeneralError (message, data) { - FeathersError.call(this, message, 'GeneralError', 500, 'general-error', data); -} - -inheritsFrom(GeneralError, FeathersError); - -// 501 - Not Implemented -function NotImplemented (message, data) { - FeathersError.call(this, message, 'NotImplemented', 501, 'not-implemented', data); -} - -inheritsFrom(NotImplemented, FeathersError); - -// 502 - Bad Gateway -function BadGateway (message, data) { - FeathersError.call(this, message, 'BadGateway', 502, 'bad-gateway', data); -} - -inheritsFrom(BadGateway, FeathersError); - -// 503 - Unavailable -function Unavailable (message, data) { - FeathersError.call(this, message, 'Unavailable', 503, 'unavailable', data); -} - -inheritsFrom(Unavailable, FeathersError); - -const errors = { - FeathersError, - BadRequest, - NotAuthenticated, - PaymentError, - Forbidden, - NotFound, - MethodNotAllowed, - NotAcceptable, - Timeout, - Conflict, - Gone, - LengthRequired, - Unprocessable, - TooManyRequests, - GeneralError, - NotImplemented, - BadGateway, - Unavailable, - 400: BadRequest, - 401: NotAuthenticated, - 402: PaymentError, - 403: Forbidden, - 404: NotFound, - 405: MethodNotAllowed, - 406: NotAcceptable, - 408: Timeout, - 409: Conflict, - 410: Gone, - 411: LengthRequired, - 422: Unprocessable, - 429: TooManyRequests, - 500: GeneralError, - 501: NotImplemented, - 502: BadGateway, - 503: Unavailable -}; - -function convert (error) { - if (!error) { - return error; - } - - const FeathersError = errors[error.name]; - const result = FeathersError - ? new FeathersError(error.message, error.data) - : new Error(error.message || error); - - if (typeof error === 'object') { - Object.assign(result, error); - } - - return result; -} - -module.exports = Object.assign({ convert }, errors); diff --git a/packages/errors/package.json b/packages/errors/package.json index d000256fd6..0cca9e546c 100644 --- a/packages/errors/package.json +++ b/packages/errors/package.json @@ -1,47 +1,51 @@ { - "name": "@feathersjs/errors", - "description": "Common error types for Feathers apps", - "version": "4.5.2", - "homepage": "https://feathersjs.com", - "main": "lib/index", - "types": "index.d.ts", - "keywords": [ - "feathers", - "feathers-plugin" - ], - "license": "MIT", - "repository": { - "type": "git", - "url": "git://github.com/feathersjs/feathers.git" - }, - "author": { - "name": "Feathers contributors", - "email": "hello@feathersjs.com", - "url": "https://feathersjs.com" - }, - "contributors": [], - "bugs": { - "url": "https://github.com/feathersjs/feathers/issues" - }, - "engines": { - "node": ">= 10" - }, - "directories": { - "lib": "lib" - }, - "scripts": { - "test": "mocha --config ../../.mocharc.json" - }, - "publishConfig": { - "access": "public" - }, - "dependencies": { - "debug": "^4.1.1" - }, - "devDependencies": { - "@feathersjs/feathers": "^4.5.2", - "express": "^4.17.1", - "mocha": "^7.1.1" - }, - "gitHead": "9b9f0f13387341bdd320f1e66feda828fca2c9f2" -} + "name": "@feathersjs/errors", + "description": "Common error types for Feathers apps", + "version": "4.5.2", + "homepage": "https://feathersjs.com", + "main": "lib/", + "types": "lib/", + "keywords": [ + "feathers", + "feathers-plugin" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "git://github.com/feathersjs/feathers.git" + }, + "author": { + "name": "Feathers contributors", + "email": "hello@feathersjs.com", + "url": "https://feathersjs.com" + }, + "contributors": [], + "bugs": { + "url": "https://github.com/feathersjs/feathers/issues" + }, + "engines": { + "node": ">= 10" + }, + "directories": { + "lib": "lib" + }, + "scripts": { + "prepublish": "npm run compile", + "compile": "shx rm -rf lib/ && tsc", + "test": "mocha --config ../../.mocharc.ts.json --recursive test/**.test.ts test/**/*.test.ts" + }, + "publishConfig": { + "access": "public" + }, + "dependencies": {}, + "devDependencies": { + "@feathersjs/feathers": "^4.5.2", + "@types/mocha": "^7.0.2", + "@types/node": "^13.11.1", + "mocha": "^7.1.1", + "shx": "^0.3.2", + "ts-node": "^8.8.2", + "typescript": "^3.8.3" + }, + "gitHead": "9b9f0f13387341bdd320f1e66feda828fca2c9f2" +} \ No newline at end of file diff --git a/packages/errors/src/index.ts b/packages/errors/src/index.ts new file mode 100644 index 0000000000..25c692bcf3 --- /dev/null +++ b/packages/errors/src/index.ts @@ -0,0 +1,273 @@ +export interface FeathersErrorJSON { + name: string; + message: string; + code: number; + className: string; + data?: any; + errors?: any; +} + +export type DynamicError = Error & { [key: string]: any }; +export type ErrorMessage = string | DynamicError | { [key: string]: any } | any[]; + +interface ErrorProperties extends Omit { + type: string; +} + +export class FeathersError extends Error { + readonly type: string; + readonly code: number; + readonly className: string; + readonly data: any; + readonly errors: any; + + constructor (err: ErrorMessage, name: string, code: number, className: string, _data: any) { + let msg = typeof err === 'string' ? err : 'Error'; + const properties: ErrorProperties = { + name, + code, + className, + type: 'FeathersError' + }; + + if (Array.isArray(_data)) { + properties.data = _data; + } else if (typeof err === 'object' || _data !== undefined) { + const { message, errors, ...rest } = typeof err === 'object' ? err : _data; + + msg = message || msg; + properties.errors = errors; + properties.data = rest; + } + + super(msg); + Object.assign(this, properties); + } + + toJSON () { + const result: FeathersErrorJSON = { + name: this.name, + message: this.message, + code: this.code, + className: this.className + }; + + if (this.data !== undefined) { + result.data = this.data; + } + + if (this.errors !== undefined) { + result.errors = this.errors; + } + + return result; + } +} + +export class BadRequest extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'BadRequest', 400, 'bad-request', data); + } +} + +// 401 - Not Authenticated +export class NotAuthenticated extends FeathersError{ + constructor (message?: ErrorMessage, data?: any) { + super(message, 'NotAuthenticated', 401, 'not-authenticated', data); + } +} + +// 402 - Payment Error +export class PaymentError extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'PaymentError', 402, 'payment-error', data); + } +} + +// 403 - Forbidden +export class Forbidden extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'Forbidden', 403, 'forbidden', data); + } +} + +// 404 - Not Found +export class NotFound extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'NotFound', 404, 'not-found', data); + } +} + +// 405 - Method Not Allowed +export class MethodNotAllowed extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'MethodNotAllowed', 405, 'method-not-allowed', data); + } +} + +// 406 - Not Acceptable +export class NotAcceptable extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'NotAcceptable', 406, 'not-acceptable', data); + } +} + +// 408 - Timeout +export class Timeout extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'Timeout', 408, 'timeout', data); + } +} + +// 409 - Conflict +export class Conflict extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'Conflict', 409, 'conflict', data); + } +} + +// 410 - Gone +export class Gone extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'Gone', 410, 'gone', data); + } +} + +// 411 - Length Required +export class LengthRequired extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'LengthRequired', 411, 'length-required', data); + } +} + +// 422 Unprocessable +export class Unprocessable extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'Unprocessable', 422, 'unprocessable', data); + } +} + +// 429 Too Many Requests +export class TooManyRequests extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'TooManyRequests', 429, 'too-many-requests', data); + } +} + +// 500 - General Error +export class GeneralError extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'GeneralError', 500, 'general-error', data); + } +} + +// 501 - Not Implemented +export class NotImplemented extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'NotImplemented', 501, 'not-implemented', data); + } +} + +// 502 - Bad Gateway +export class BadGateway extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'BadGateway', 502, 'bad-gateway', data); + } +} + +// 503 - Unavailable +export class Unavailable extends FeathersError { + constructor (message?: ErrorMessage, data?: any) { + super(message, 'Unavailable', 503, 'unavailable', data); + } +} + +export interface Errors { + FeathersError: FeathersError; + BadRequest: BadRequest; + NotAuthenticated: NotAuthenticated; + PaymentError: PaymentError; + Forbidden: Forbidden; + NotFound: NotFound; + MethodNotAllowed: MethodNotAllowed; + NotAcceptable: NotAcceptable; + Timeout: Timeout; + Conflict: Conflict; + LengthRequired: LengthRequired; + Unprocessable: Unprocessable; + TooManyRequests: TooManyRequests; + GeneralError: GeneralError; + NotImplemented: NotImplemented; + BadGateway: BadGateway; + Unavailable: Unavailable; + 400: BadRequest; + 401: NotAuthenticated; + 402: PaymentError; + 403: Forbidden; + 404: NotFound; + 405: MethodNotAllowed; + 406: NotAcceptable; + 408: Timeout; + 409: Conflict; + 411: LengthRequired; + 422: Unprocessable; + 429: TooManyRequests; + 500: GeneralError; + 501: NotImplemented; + 502: BadGateway; + 503: Unavailable; +} + +export const errors = { + FeathersError, + BadRequest, + NotAuthenticated, + PaymentError, + Forbidden, + NotFound, + MethodNotAllowed, + NotAcceptable, + Timeout, + Conflict, + LengthRequired, + Unprocessable, + TooManyRequests, + GeneralError, + NotImplemented, + BadGateway, + Unavailable, + 400: BadRequest, + 401: NotAuthenticated, + 402: PaymentError, + 403: Forbidden, + 404: NotFound, + 405: MethodNotAllowed, + 406: NotAcceptable, + 408: Timeout, + 409: Conflict, + 410: Gone, + 411: LengthRequired, + 422: Unprocessable, + 429: TooManyRequests, + 500: GeneralError, + 501: NotImplemented, + 502: BadGateway, + 503: Unavailable +} + +export function convert (error: any) { + if (!error) { + return error; + } + + const FeathersError = (errors as any)[error.name]; + const result = FeathersError + ? new FeathersError(error.message, error.data) + : new Error(error.message || error); + + if (typeof error === 'object') { + Object.assign(result, error); + } + + return result; +} diff --git a/packages/errors/test/index.test.js b/packages/errors/test/index.test.ts similarity index 73% rename from packages/errors/test/index.test.js rename to packages/errors/test/index.test.ts index 1b14ae4bc1..e0a1760691 100644 --- a/packages/errors/test/index.test.js +++ b/packages/errors/test/index.test.ts @@ -1,14 +1,9 @@ -const assert = require('assert'); +import assert from 'assert'; +import * as errors from '../src'; -const errors = require('../lib'); const { convert } = errors; describe('@feathersjs/errors', () => { - it('is CommonJS compatible', () => { - assert.strictEqual(typeof require('../lib'), 'object'); - assert.strictEqual(typeof require('../lib').FeathersError, 'function'); - }); - describe('errors.convert', () => { it('converts objects to feathers errors', () => { const error = convert({ @@ -111,80 +106,78 @@ describe('@feathersjs/errors', () => { }); it('400', () => { - assert.notStrictEqual(typeof errors[400], 'undefined', 'has BadRequest alias'); + assert.notStrictEqual(typeof errors.errors[400], 'undefined', 'has BadRequest alias'); }); it('401', () => { - assert.notStrictEqual(typeof errors[401], 'undefined', 'has NotAuthenticated alias'); + assert.notStrictEqual(typeof errors.errors[401], 'undefined', 'has NotAuthenticated alias'); }); it('402', () => { - assert.notStrictEqual(typeof errors[402], 'undefined', 'has PaymentError alias'); + assert.notStrictEqual(typeof errors.errors[402], 'undefined', 'has PaymentError alias'); }); it('403', () => { - assert.notStrictEqual(typeof errors[403], 'undefined', 'has Forbidden alias'); + assert.notStrictEqual(typeof errors.errors[403], 'undefined', 'has Forbidden alias'); }); it('404', () => { - assert.notStrictEqual(typeof errors[404], 'undefined', 'has NotFound alias'); + assert.notStrictEqual(typeof errors.errors[404], 'undefined', 'has NotFound alias'); }); it('405', () => { - assert.notStrictEqual(typeof errors[405], 'undefined', 'has MethodNotAllowed alias'); + assert.notStrictEqual(typeof errors.errors[405], 'undefined', 'has MethodNotAllowed alias'); }); it('406', () => { - assert.notStrictEqual(typeof errors[406], 'undefined', 'has NotAcceptable alias'); + assert.notStrictEqual(typeof errors.errors[406], 'undefined', 'has NotAcceptable alias'); }); it('408', () => { - assert.notStrictEqual(typeof errors[408], 'undefined', 'has Timeout alias'); + assert.notStrictEqual(typeof errors.errors[408], 'undefined', 'has Timeout alias'); }); it('409', () => { - assert.notStrictEqual(typeof errors[409], 'undefined', 'has Conflict alias'); + assert.notStrictEqual(typeof errors.errors[409], 'undefined', 'has Conflict alias'); }); it('410', () => { - assert.notStrictEqual(typeof errors[410], 'undefined', 'has Gone alias'); + assert.notStrictEqual(typeof errors.errors[410], 'undefined', 'has Gone alias'); }); it('411', () => { - assert.notStrictEqual(typeof errors[411], 'undefined', 'has LengthRequired alias'); + assert.notStrictEqual(typeof errors.errors[411], 'undefined', 'has LengthRequired alias'); }); it('422', () => { - assert.notStrictEqual(typeof errors[422], 'undefined', 'has Unprocessable alias'); + assert.notStrictEqual(typeof errors.errors[422], 'undefined', 'has Unprocessable alias'); }); it('429', () => { - assert.notStrictEqual(typeof errors[429], 'undefined', 'has TooManyRequests alias'); + assert.notStrictEqual(typeof errors.errors[429], 'undefined', 'has TooManyRequests alias'); }); it('500', () => { - assert.notStrictEqual(typeof errors[500], 'undefined', 'has GeneralError alias'); + assert.notStrictEqual(typeof errors.errors[500], 'undefined', 'has GeneralError alias'); }); it('501', () => { - assert.notStrictEqual(typeof errors[501], 'undefined', 'has NotImplemented alias'); + assert.notStrictEqual(typeof errors.errors[501], 'undefined', 'has NotImplemented alias'); }); it('502', () => { - assert.notStrictEqual(typeof errors[502], 'undefined', 'has BadGateway alias'); + assert.notStrictEqual(typeof errors.errors[502], 'undefined', 'has BadGateway alias'); }); it('503', () => { - assert.notStrictEqual(typeof errors[503], 'undefined', 'has Unavailable alias'); + assert.notStrictEqual(typeof errors.errors[503], 'undefined', 'has Unavailable alias'); }); it('instantiates every error', () => { - Object.keys(errors).forEach(name => { - if (name === 'convert') { - return; - } + const index: any = errors.errors; - const E = errors[name]; + Object.keys(index).forEach(name => { + const E = index[name]; if (E) { // tslint:disable-next-line @@ -216,23 +209,25 @@ describe('@feathersjs/errors', () => { describe('successful error creation', () => { describe('without custom message', () => { it('default error', () => { - var error = new errors.GeneralError(); + const error = new errors.GeneralError(); assert.strictEqual(error.code, 500); assert.strictEqual(error.className, 'general-error'); assert.strictEqual(error.message, 'Error'); + assert.strictEqual(error.data, undefined); + assert.strictEqual(error.errors, undefined); assert.notStrictEqual(error.stack, undefined); assert.strictEqual(error instanceof errors.GeneralError, true); assert.strictEqual(error instanceof errors.FeathersError, true); }); it('can wrap an existing error', () => { - var error = new errors.BadRequest(new Error()); + const error = new errors.BadRequest(new Error()); assert.strictEqual(error.code, 400); assert.strictEqual(error.message, 'Error'); }); it('with multiple errors', () => { - var data = { + const data = { errors: { email: 'Email Taken', password: 'Invalid Password' @@ -240,7 +235,7 @@ describe('@feathersjs/errors', () => { foo: 'bar' }; - var error = new errors.BadRequest(data); + const error = new errors.BadRequest(data); assert.strictEqual(error.code, 400); assert.strictEqual(error.message, 'Error'); assert.deepStrictEqual(error.errors, { email: 'Email Taken', password: 'Invalid Password' }); @@ -248,12 +243,12 @@ describe('@feathersjs/errors', () => { }); it('with data', () => { - var data = { + const data = { email: 'Email Taken', password: 'Invalid Password' }; - var error = new errors.GeneralError(data); + const error = new errors.GeneralError(data); assert.strictEqual(error.code, 500); assert.strictEqual(error.message, 'Error'); assert.deepStrictEqual(error.data, data); @@ -262,31 +257,31 @@ describe('@feathersjs/errors', () => { describe('with custom message', () => { it('contains our message', () => { - var error = new errors.BadRequest('Invalid Password'); + const error = new errors.BadRequest('Invalid Password'); assert.strictEqual(error.code, 400); assert.strictEqual(error.message, 'Invalid Password'); }); it('can wrap an existing error', () => { - var error = new errors.BadRequest(new Error('Invalid Password')); + const error = new errors.BadRequest(new Error('Invalid Password')); assert.strictEqual(error.code, 400); assert.strictEqual(error.message, 'Invalid Password'); }); it('with data', () => { - var data = { + const data = { email: 'Email Taken', password: 'Invalid Password' }; - var error = new errors.GeneralError('Custom Error', data); + const error = new errors.GeneralError('Custom Error', data); assert.strictEqual(error.code, 500); assert.strictEqual(error.message, 'Custom Error'); assert.deepStrictEqual(error.data, data); }); it('with multiple errors', () => { - var data = { + const data = { errors: { email: 'Email Taken', password: 'Invalid Password' @@ -294,7 +289,8 @@ describe('@feathersjs/errors', () => { foo: 'bar' }; - var error = new errors.BadRequest(data); + const error = new errors.BadRequest(data); + assert.strictEqual(error.code, 400); assert.strictEqual(error.message, 'Error'); assert.deepStrictEqual(error.errors, { email: 'Email Taken', password: 'Invalid Password' }); @@ -303,7 +299,7 @@ describe('@feathersjs/errors', () => { }); it('can return JSON', () => { - var data = { + const data = { errors: { email: 'Email Taken', password: 'Invalid Password' @@ -311,14 +307,14 @@ describe('@feathersjs/errors', () => { foo: 'bar' }; - var expected = '{"name":"GeneralError","message":"Custom Error","code":500,"className":"general-error","data":{"foo":"bar"},"errors":{"email":"Email Taken","password":"Invalid Password"}}'; + const expected = '{"name":"GeneralError","message":"Custom Error","code":500,"className":"general-error","data":{"foo":"bar"},"errors":{"email":"Email Taken","password":"Invalid Password"}}'; - var error = new errors.GeneralError('Custom Error', data); + const error = new errors.GeneralError('Custom Error', data); assert.strictEqual(JSON.stringify(error), expected); }); it('can handle immutable data', () => { - var data = { + const data = { errors: { email: 'Email Taken', password: 'Invalid Password' @@ -326,24 +322,22 @@ describe('@feathersjs/errors', () => { foo: 'bar' }; - var error = new errors.GeneralError('Custom Error', Object.freeze(data)); + const error = new errors.GeneralError('Custom Error', Object.freeze(data)); assert.strictEqual(error.data.errors, undefined); assert.deepStrictEqual(error.data, { foo: 'bar' }); }); it('allows arrays as data', () => { - var data = [ + const data = [ { hello: 'world' } ]; - data.errors = 'Invalid input'; - var error = new errors.GeneralError('Custom Error', data); + const error = new errors.GeneralError('Custom Error', data); assert.strictEqual(error.data.errors, undefined); assert.ok(Array.isArray(error.data)); assert.deepStrictEqual(error.data, [{ hello: 'world' }]); - assert.strictEqual(error.errors, 'Invalid input'); }); it('has proper stack trace (#78)', () => { @@ -354,7 +348,7 @@ describe('@feathersjs/errors', () => { assert.strictEqual(e.stack.indexOf(text), 0); - assert.ok(e.stack.indexOf('index.test.js') !== -1); + assert.ok(e.stack.indexOf('index.test.ts') !== -1); const oldCST = Error.captureStackTrace; @@ -369,4 +363,4 @@ describe('@feathersjs/errors', () => { } }); }); -}); +}); \ No newline at end of file diff --git a/packages/errors/tsconfig.json b/packages/errors/tsconfig.json new file mode 100644 index 0000000000..316fd41336 --- /dev/null +++ b/packages/errors/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig", + "include": [ + "src/**/*.ts" + ], + "compilerOptions": { + "outDir": "lib" + } +} diff --git a/packages/express/lib/error-handler.js b/packages/express/lib/error-handler.js index a8dbfd2a30..8780422ab5 100644 --- a/packages/express/lib/error-handler.js +++ b/packages/express/lib/error-handler.js @@ -37,9 +37,10 @@ module.exports = function (options = {}) { if (error.type !== 'FeathersError') { let oldError = error; - error = new errors.GeneralError(oldError.message, { + + error = oldError.errors ? new errors.GeneralError(oldError.message, { errors: oldError.errors - }); + }) : new errors.GeneralError(oldError.message); if (oldError.stack) { error.stack = oldError.stack; diff --git a/packages/express/test/error-handler.test.js b/packages/express/test/error-handler.test.js index ba649feee9..3795bf8dbb 100644 --- a/packages/express/test/error-handler.test.js +++ b/packages/express/test/error-handler.test.js @@ -3,7 +3,7 @@ const { strict: assert } = require('assert'); const express = require('express'); const errors = require('@feathersjs/errors'); -const request = require('request'); +const axios = require('axios'); const fs = require('fs'); const { join } = require('path'); @@ -51,30 +51,6 @@ describe('error-handler', () => { this.server.close(done); }); - describe('HTML handler', () => { - const options = { - url: 'http://localhost:5050/error', - headers: { - 'Content-Type': 'text/html', - 'Accept': 'text/html' - } - }; - - it('logs the error', done => { - request(options, (error, res, body) => { - assert.equal(currentError.message, 'Something went wrong'); - done(); - }); - }); - - it('can send a custom response', done => { - request(options, (error, res, body) => { - assert.equal(body, content); - done(); - }); - }); - }); - describe('JSON handler', () => { const options = { url: 'http://localhost:5050/error', @@ -84,19 +60,18 @@ describe('error-handler', () => { } }; - it('can send a custom response', done => { - const expected = JSON.stringify({ - name: 'GeneralError', - message: 'Something went wrong', - code: 500, - className: 'general-error', - data: {}, - errors: {} - }); - request(options, (error, res, body) => { - assert.deepEqual(body, expected); - done(); - }); + it('can send a custom response', async () => { + try { + await axios(options); + assert.fail('Should never get here'); + } catch (error) { + assert.deepEqual(error.response.data, { + name: 'GeneralError', + message: 'Something went wrong', + code: 500, + className: 'general-error' + }); + } }); }); }); @@ -258,233 +233,153 @@ describe('error-handler', () => { }); describe('converts an non-feathers error', () => { - it('is an instance of GeneralError', done => { - request({ - url: 'http://localhost:5050/error', - json: true - }, (error, res, body) => { - assert.equal(res.statusCode, 500); - assert.deepEqual(body, { + it('is an instance of GeneralError', async () => { + try { + await axios({ + url: 'http://localhost:5050/error', + json: true + }); + assert.fail('Should never get here'); + } catch (error) { + assert.equal(error.response.status, 500); + assert.deepEqual(error.response.data, { name: 'GeneralError', message: 'Something went wrong', code: 500, - className: 'general-error', - data: {}, - errors: {} + className: 'general-error' }); - done(); - }); + } }); }); describe('text/html format', () => { it('serves a 404.html', done => { - fs.readFile(join(__dirname, '..', 'lib', 'public', '404.html'), function (err, html) { - request({ - url: 'http://localhost:5050/path/to/nowhere', - headers: { - 'Content-Type': 'text/html', - 'Accept': 'text/html' - } - }, (error, res, body) => { - assert.equal(res.statusCode, 404); - assert.equal(html.toString(), body); + fs.readFile(join(__dirname, '..', 'lib', 'public', '404.html'), async function (err, html) { + try { + await axios({ + url: 'http://localhost:5050/path/to/nowhere', + headers: { + 'Content-Type': 'text/html', + 'Accept': 'text/html' + } + }); + assert.fail('Should never get here'); + } catch(error) { + assert.equal(error.response.status, 404); + assert.equal(error.response.data, html.toString()); done(); - }); + } }); }); it('serves a 500.html', done => { - fs.readFile(join(__dirname, '..', 'lib', 'public', 'default.html'), function (err, html) { - request({ - url: 'http://localhost:5050/error', - headers: { - 'Content-Type': 'text/html', - 'Accept': 'text/html' - } - }, (error, res, body) => { - assert.equal(res.statusCode, 500); - assert.equal(html.toString(), body); - done(); - }); - }); - }); - - it('returns html when Content-Type header is set', done => { - fs.readFile(join(__dirname, '..', 'lib', 'public', '404.html'), function (err, html) { - request({ - url: 'http://localhost:5050/path/to/nowhere', - headers: { - 'Content-Type': 'text/html' - } - }, (error, res, body) => { - assert.equal(res.statusCode, 404); - assert.equal(html.toString(), body); + fs.readFile(join(__dirname, '..', 'lib', 'public', 'default.html'), async function (err, html) { + try { + await axios({ + url: 'http://localhost:5050/error', + headers: { + 'Content-Type': 'text/html', + 'Accept': 'text/html' + } + }); + assert.fail('Should never get here'); + } catch(error) { + assert.equal(error.response.status, 500); + assert.equal(error.response.data, html.toString()); done(); - }); + } }); }); + }); - it('returns html when Accept header is set', done => { - fs.readFile(join(__dirname, '..', 'lib', 'public', '404.html'), function (err, html) { - request({ - url: 'http://localhost:5050/path/to/nowhere', + describe('application/json format', () => { + it('500', async () => { + try { + await axios({ + url: 'http://localhost:5050/error', headers: { - 'Accept': 'text/html' + 'Content-Type': 'application/json', + 'Accept': 'application/json' } - }, (error, res, body) => { - assert.equal(res.statusCode, 404); - assert.equal(html.toString(), body); - done(); }); - }); - }); - }); - - describe('application/json format', () => { - it('500', done => { - request({ - url: 'http://localhost:5050/error', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - json: true - }, (error, res, body) => { - assert.equal(res.statusCode, 500); - assert.deepEqual(body, { + assert.fail('Should never get here'); + } catch (error) { + assert.equal(error.response.status, 500); + assert.deepEqual(error.response.data, { name: 'GeneralError', message: 'Something went wrong', code: 500, - className: 'general-error', - data: {}, - errors: {} + className: 'general-error' }); - done(); - }); + } }); - it('404', done => { - request({ - url: 'http://localhost:5050/path/to/nowhere', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - json: true - }, (error, res, body) => { - assert.equal(res.statusCode, 404); - assert.deepEqual(body, { name: 'NotFound', + it('404', async () => { + try { + await axios({ + url: 'http://localhost:5050/path/to/nowhere', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }); + assert.fail('Should never get here'); + } catch (error) { + assert.equal(error.response.status, 404); + assert.deepEqual(error.response.data, { name: 'NotFound', message: 'File not found', code: 404, - className: 'not-found', - errors: {} + className: 'not-found' }); - done(); - }); - }); - - it('400', done => { - request({ - url: 'http://localhost:5050/bad-request', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json' - }, - json: true - }, (error, res, body) => { - assert.equal(res.statusCode, 400); - assert.deepEqual(body, { name: 'BadRequest', - message: 'Invalid Password', - code: 400, - className: 'bad-request', - data: { - message: 'Invalid Password' - }, - errors: [{ - path: 'password', - value: null, - message: `'password' cannot be 'null'` - }] - }); - done(); - }); + } }); - it('returns JSON when only Content-Type header is set', done => { - request({ - url: 'http://localhost:5050/bad-request', - headers: { - 'Content-Type': 'application/json' - }, - json: true - }, (error, res, body) => { - assert.equal(res.statusCode, 400); - assert.deepEqual(body, { name: 'BadRequest', - message: 'Invalid Password', - code: 400, - className: 'bad-request', - data: { - message: 'Invalid Password' - }, - errors: [{ - path: 'password', - value: null, - message: `'password' cannot be 'null'` - }] + it('400', async () => { + try { + await axios({ + url: 'http://localhost:5050/bad-request', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } }); - done(); - }); - }); - - it('returns JSON when only Accept header is set', done => { - request({ - url: 'http://localhost:5050/bad-request', - headers: { - 'Accept': 'application/json' - }, - json: true - }, (error, res, body) => { - assert.equal(res.statusCode, 400); - assert.deepEqual(body, { name: 'BadRequest', + assert.fail('Should never get here'); + } catch (error) { + assert.equal(error.response.status, 400); + assert.deepEqual(error.response.data, { name: 'BadRequest', message: 'Invalid Password', code: 400, className: 'bad-request', - data: { - message: 'Invalid Password' - }, + data: {}, errors: [{ path: 'password', value: null, message: `'password' cannot be 'null'` }] }); - done(); - }); + } }); }); - it('returns JSON by default', done => { - request('http://localhost:5050/bad-request', (error, res, body) => { - const expected = JSON.stringify({ + it('returns JSON by default', async () => { + try { + await axios('http://localhost:5050/bad-request'); + assert.fail('Should never get here'); + } catch (error) { + assert.equal(error.response.status, 400); + assert.deepEqual(error.response.data, { name: 'BadRequest', message: 'Invalid Password', code: 400, className: 'bad-request', - data: { - message: 'Invalid Password' - }, + data: {}, errors: [{ path: 'password', value: null, message: `'password' cannot be 'null'` }] }); - - assert.equal(res.statusCode, 400); - assert.deepEqual(body, expected); - done(); - }); + } }); }); }); diff --git a/packages/express/test/rest.test.js b/packages/express/test/rest.test.js index 5e1cd9fcb6..5f236e23fe 100644 --- a/packages/express/test/rest.test.js +++ b/packages/express/test/rest.test.js @@ -118,6 +118,7 @@ describe('@feathersjs/express/rest provider', () => { const convertHook = hook => { const result = Object.assign({}, hook); + delete result.self; delete result.service; delete result.app; delete result.error; @@ -576,8 +577,7 @@ describe('@feathersjs/express/rest provider', () => { name: 'BadRequest', message: 'Not good', code: 400, - className: 'bad-request', - errors: {} + className: 'bad-request' }); }); }); diff --git a/packages/feathers/lib/events.js b/packages/feathers/lib/events.js index 96e9e75b3c..9ed3c84d09 100644 --- a/packages/feathers/lib/events.js +++ b/packages/feathers/lib/events.js @@ -4,16 +4,16 @@ const Proto = require('uberproto'); // Returns a hook that emits service events. Should always be // used as the very last hook in the chain const eventHook = exports.eventHook = function eventHook () { - return function (hook) { - const { app, service } = hook; - const eventName = hook.event === null ? hook.event : app.eventMappings[hook.method]; + return function (ctx) { + const { app, service, method, event, type, result } = ctx; + const eventName = event === null ? event : app.eventMappings[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') { - const results = Array.isArray(hook.result) ? hook.result : [ hook.result ]; + if (eventName && isHookEvent && type !== 'error') { + const results = Array.isArray(result) ? result : [ result ]; - results.forEach(element => service.emit(eventName, element, hook)); + results.forEach(element => service.emit(eventName, element, ctx)); } }; }; diff --git a/packages/feathers/lib/hooks/base.js b/packages/feathers/lib/hooks/base.js index bde4fea210..15be029670 100644 --- a/packages/feathers/lib/hooks/base.js +++ b/packages/feathers/lib/hooks/base.js @@ -1,6 +1,6 @@ const { _ } = require('@feathersjs/commons'); -const assignArguments = context => { +const assignArguments = (context, next) => { const { service, method } = context; const parameters = service.methods[method]; @@ -12,10 +12,10 @@ const assignArguments = context => { context.params = {}; } - return context; + return next(); }; -const validate = context => { +const validate = (context, next) => { const { service, method, path } = context; const parameters = service.methods[method]; @@ -27,7 +27,7 @@ const validate = context => { throw new Error(`A data object must be provided to the '${path}.${method}' method`); } - return context; + return next(); }; module.exports = [ assignArguments, validate ]; diff --git a/packages/feathers/lib/hooks/index.js b/packages/feathers/lib/hooks/index.js index e1db2eb6f6..5f2ebf574e 100644 --- a/packages/feathers/lib/hooks/index.js +++ b/packages/feathers/lib/hooks/index.js @@ -1,132 +1,110 @@ -const { hooks, isPromise } = require('@feathersjs/commons'); +const { hooks: hookCommons } = require('@feathersjs/commons'); +const { + hooks: hooksDecorator, + HookContext, + getMiddleware, + withParams, + withProps +} = require('@feathersjs/hooks'); const baseHooks = require('./base'); const { - createHookObject, getHooks, - processHooks, enableHooks, - ACTIVATE_HOOKS -} = hooks; + ACTIVATE_HOOKS, + finallyWrapper, + errorWrapper, + wrap +} = hookCommons; + +function getContextUpdaters (app, service, method) { + const parameters = service.methods[method].map(v => (v === 'params' ? ['params', {}] : v)); + + return [ + withParams(...parameters), + withProps({ + app, + service, + type: 'before', + get path () { + if (!service || !app || !app.services) { + return null; + } + + return Object.keys(app.services) + .find(path => app.services[path] === service); + } + }) + ]; +} + +function getCollector (app, service, method) { + return (self, fn, args) => { + const middleware = [ + ...getMiddleware(self), + ...(fn && typeof fn.collect === 'function' ? fn.collect(fn, fn.original, args) : []) + ]; + + if (typeof self === 'object') { + return middleware; + } -const withHooks = function withHooks ({ - app, - service, - method, - original -}) { - return (_hooks = {}) => { - const hooks = app.hookTypes.reduce((result, type) => { - const value = _hooks[type] || []; + const hooks = { + async: getHooks(app, service, 'async', method), + before: getHooks(app, service, 'before', method), + after: getHooks(app, service, 'after', method, true), + error: getHooks(app, service, 'error', method, true), + finally: getHooks(app, service, 'finally', method, true) + }; - result[type] = Array.isArray(value) ? value : [ value ]; + return [ + ...finallyWrapper(hooks.finally), + ...errorWrapper(hooks.error), + ...baseHooks, + ...middleware, + ...wrap(hooks) + ]; + }; +} - return result; - }, {}); - - return function (...args) { - const returnHook = args[args.length - 1] === true - ? args.pop() : false; - - // Create the hook object that gets passed through - const hookObject = createHookObject(method, { - type: 'before', // initial hook object type - arguments: args, - service, - app - }); - - return Promise.resolve(hookObject) - - // Run `before` hooks - .then(hookObject => { - return processHooks.call(service, baseHooks.concat(hooks.before), hookObject); - }) - - // Run 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 = new Promise(resolve => { - const func = original || service[method]; - const args = service.methods[method].map((value) => hookObject[value]); - const result = func.apply(service, args); - - if (!isPromise(result)) { - throw new Error(`Service method '${hookObject.method}' for '${hookObject.path}' service must return a promise`); - } - - resolve(result); - }); - - return promise - .then(result => { - hookObject.result = result; - return hookObject; - }) - .catch(error => { - error.hook = hookObject; - throw error; - }); - }) - - // Run `after` hooks - .then(hookObject => { - const afterHookObject = Object.assign({}, hookObject, { - type: 'after' - }); - - return processHooks.call(service, hooks.after, afterHookObject); - }) - - // Run `errors` hooks - .catch(error => { - const errorHookObject = Object.assign({}, error.hook, { - type: 'error', - original: error.hook, - error, - result: undefined - }); - - return processHooks.call(service, hooks.error, errorHookObject) - .catch(error => { - const errorHookObject = Object.assign({}, error.hook, { - error, - result: undefined - }); - - return errorHookObject; - }); - }) - - // Run `finally` hooks - .then(hookObject => { - return processHooks.call(service, hooks.finally, hookObject) - .catch(error => { - const errorHookObject = Object.assign({}, error.hook, { - error, - result: undefined - }); - - return errorHookObject; - }); - }) - - // Resolve with a result or reject with an error - .then(hookObject => { - if (typeof hookObject.error !== 'undefined' && typeof hookObject.result === 'undefined') { - return Promise.reject(returnHook ? hookObject : hookObject.error); - } else { - return returnHook ? hookObject : hookObject.result; - } - }); +function withHooks (app, service, methods) { + const hookMap = methods.reduce((accu, method) => { + if (typeof service[method] !== 'function') { + return accu; + } + + accu[method] = { + middleware: [], + context: getContextUpdaters(app, service, method), + collect: getCollector(app, service, method) }; - }; -}; + + return accu; + }, {}); + + hooksDecorator(service, hookMap); +} + +function mixinMethod () { + const service = this; + const args = Array.from(arguments); + + const returnHook = args[args.length - 1] === true || args[args.length - 1] instanceof HookContext + ? args.pop() : false; + + const hookContext = returnHook instanceof HookContext ? returnHook : new HookContext(); + + return this._super.call(service, ...args, hookContext) + .then(() => returnHook ? hookContext : hookContext.result) + // Handle errors + .catch(() => { + if (typeof hookContext.error !== 'undefined' && typeof hookContext.result === 'undefined') { + return Promise.reject(returnHook ? hookContext : hookContext.error); + } else { + return returnHook ? hookContext : hookContext.result; + } + }); +} // A service mixin that adds `service.hooks()` method and functionality const hookMixin = exports.hookMixin = function hookMixin (service) { @@ -152,29 +130,16 @@ const hookMixin = exports.hookMixin = function hookMixin (service) { const app = this; const methodNames = Object.keys(service.methods); - // Assemble the mixin object that contains all "hooked" service methods + + withHooks(app, service, methodNames); + + // Usefull only for the `returnHook` backwards compatibility with `true` const mixin = methodNames.reduce((mixin, method) => { if (typeof service[method] !== 'function') { return mixin; } - mixin[method] = function () { - const service = this; - const args = Array.from(arguments); - const original = service._super.bind(service); - - return withHooks({ - app, - service, - method, - original - })({ - before: getHooks(app, service, 'before', method), - after: getHooks(app, service, 'after', method, true), - error: getHooks(app, service, 'error', method, true), - finally: getHooks(app, service, 'finally', method, true) - })(...args); - }; + mixin[method] = mixinMethod; return mixin; }, {}); @@ -190,7 +155,7 @@ module.exports = function () { // 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'] + hookTypes: ['async', 'before', 'after', 'error', 'finally'] }); // Add functionality for hooks to be registered as app.hooks @@ -200,8 +165,6 @@ module.exports = function () { }; }; -module.exports.withHooks = withHooks; - module.exports.ACTIVATE_HOOKS = ACTIVATE_HOOKS; module.exports.activateHooks = function activateHooks (args) { diff --git a/packages/feathers/package.json b/packages/feathers/package.json index 90531e02fe..a2531e23dd 100644 --- a/packages/feathers/package.json +++ b/packages/feathers/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "@feathersjs/commons": "^4.5.2", + "@feathersjs/hooks": "^0.4.0-alpha.0", "debug": "^4.1.1", "events": "^3.1.0", "uberproto": "^2.0.6" diff --git a/packages/feathers/test/application.test.js b/packages/feathers/test/application.test.js index 36588863e1..d747c8f84a 100644 --- a/packages/feathers/test/application.test.js +++ b/packages/feathers/test/application.test.js @@ -168,6 +168,36 @@ describe('Feathers application', () => { app1.service('testing').create({ message: 'Hi' }); }); + it('async hooks', done => { + const app = feathers(); + + app.use('/dummy', { + create (data) { + return Promise.resolve(data); + } + }); + + const dummy = app.service('dummy'); + + dummy.hooks({ + async: async (ctx, next) => { + await next(); + ctx.params.fromAsyncHook = true; + }, + before: { + create (hook) { + hook.params.fromAsyncHook = false; + } + } + }); + + dummy.create({ message: 'Hi' }, {}, true) + .then(ctx => { + assert.ok(ctx.params.fromAsyncHook); + }) + .then(done, done); + }); + it('services conserve Symbols', () => { const TEST = Symbol('test'); const dummyService = { diff --git a/packages/feathers/test/hooks/app.test.js b/packages/feathers/test/hooks/app.test.js index bf9dd9a92b..2ba95ff9ed 100644 --- a/packages/feathers/test/hooks/app.test.js +++ b/packages/feathers/test/hooks/app.test.js @@ -25,6 +25,35 @@ describe('app.hooks', () => { assert.strictEqual(typeof app.hooks, 'function'); }); + describe('app.hooks({ async })', () => { + it('basic app async hook', () => { + const service = app.service('todos'); + + app.hooks({ + async async (hook, next) { + assert.strictEqual(hook.app, app); + await next(); + hook.params.ran = true; + } + }); + + return service.get('test').then(result => { + assert.deepStrictEqual(result, { + id: 'test', + params: { ran: true } + }); + + const data = { test: 'hi' }; + + return service.create(data).then(result => { + assert.deepStrictEqual(result, { + data, params: { ran: true } + }); + }); + }); + }); + }); + describe('app.hooks({ before })', () => { it('basic app before hook', () => { const service = app.service('todos'); diff --git a/packages/feathers/test/hooks/async.test.js b/packages/feathers/test/hooks/async.test.js new file mode 100644 index 0000000000..c851d022a9 --- /dev/null +++ b/packages/feathers/test/hooks/async.test.js @@ -0,0 +1,537 @@ +const assert = require('assert'); +const feathers = require('../../lib'); + +describe('`async` hooks', () => { + describe('function([hook])', () => { + it('returning a non-hook object throws error', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); + } + }); + const service = app.service('dummy'); + + service.hooks({ + async: { + get () { + return {}; + } + } + }); + + return service.get(10).catch(e => { + assert.strictEqual(e.message, 'async hook for \'get\' method returned invalid hook object'); + }); + }); + + it('hooks in chain can be replaced', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ + id, description: `You have to do ${id}` + }); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + get: [ + function (hook) { + return Object.assign({}, hook, { + modified: true + }); + }, + function (hook) { + assert.ok(hook.modified); + } + ] + } + }); + + return service.get('laundry'); + }); + + it('.async hooks can return a promise', () => { + const app = feathers().use('/dummy', { + get (id, params) { + assert.ok(params.ran, 'Ran through promise hook'); + + return Promise.resolve({ + id: id, + description: `You have to do ${id}` + }); + }, + + remove () { + assert.ok(false, 'Should never get here'); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + get (hook) { + return new Promise(resolve => { + hook.params.ran = true; + resolve(); + }); + }, + + remove () { + return new Promise((resolve, reject) => { + reject(new Error('This did not work')); + }); + } + } + }); + + return service.get('dishes').then(() => service.remove(10)) + .catch(error => { + assert.strictEqual(error.message, 'This did not work'); + }); + }); + + it('.async hooks do not need to return anything', () => { + const app = feathers().use('/dummy', { + get (id, params) { + assert.ok(params.ran, 'Ran through promise hook'); + + return Promise.resolve({ + id: id, + description: `You have to do ${id}` + }); + }, + + remove () { + assert.ok(false, 'Should never get here'); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + get (hook) { + hook.params.ran = true; + }, + + remove () { + throw new Error('This did not work'); + } + } + }); + + return service.get('dishes').then(() => service.remove(10)) + .catch(error => { + assert.strictEqual(error.message, 'This did not work'); + }); + }); + + it('.async hooks can set hook.result which will skip service method', () => { + const app = feathers().use('/dummy', { + get (id) { + assert.ok(false, 'This should never run'); + return Promise.resolve({ id }); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + async get (hook, next) { + hook.result = { + id: hook.id, + message: 'Set from hook' + }; + + await next(); + } + } + }); + + return service.get(10, {}).then(data => { + assert.deepStrictEqual(data, { + id: 10, + message: 'Set from hook' + }); + }); + }); + }); + + describe('function(hook, next)', () => { + it('gets mixed into a service and modifies data', () => { + const dummyService = { + create (data, params) { + assert.deepStrictEqual(data, { + some: 'thing', + modified: 'data' + }, 'Data modified'); + + assert.deepStrictEqual(params, { + modified: 'params' + }, 'Params modified'); + + return Promise.resolve(data); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ + async: { + create (hook, next) { + assert.strictEqual(hook.type, 'before'); + + hook.data.modified = 'data'; + + Object.assign(hook.params, { + modified: 'params' + }); + + return next(); + } + } + }); + + return service.create({ some: 'thing' }).then(data => { + assert.deepStrictEqual(data, { + some: 'thing', + modified: 'data' + }, 'Data got modified'); + }); + }); + + 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({ + async: { + create (hook, next) { + hook.data.appPresent = typeof hook.app !== 'undefined'; + assert.strictEqual(hook.data.appPresent, true); + return next(); + } + } + }); + + return someService.create({ some: 'thing' }).then(data => { + assert.deepStrictEqual(data, { + some: 'thing', + appPresent: true + }, 'App object was present'); + }); + }); + + it('passes errors', () => { + const dummyService = { + update () { + assert.ok(false, 'Never should be called'); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ + async: { + update () { + throw new Error('You are not allowed to update'); + } + } + }); + + return service.update(1, {}).catch(error => { + assert.ok(error, 'Got an error'); + assert.strictEqual(error.message, 'You are not allowed to update', 'Got error message'); + }); + }); + + it('does not run after hook when there is an error', () => { + 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: { + async remove (context, next) { + await next(); + + assert.ok(false, 'This should never get called'); + } + } + }); + + return service.remove(1, {}).catch(error => { + assert.ok(error, 'Got error'); + assert.strictEqual(error.message, 'Error removing item', 'Got error message from service'); + }); + }); + + it('calling back with no arguments uses the old ones', () => { + const dummyService = { + remove (id, params) { + assert.strictEqual(id, 1, 'Got id'); + assert.deepStrictEqual(params, { my: 'param' }); + + return Promise.resolve({ id }); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ + async: { + remove (hook, next) { + next(); + } + } + }); + + return service.remove(1, { my: 'param' }); + }); + + it('adds .hooks() and chains multiple hooks for the same method', () => { + const dummyService = { + create (data, params) { + assert.deepStrictEqual(data, { + some: 'thing', + modified: 'second data' + }, 'Data modified'); + + assert.deepStrictEqual(params, { + modified: 'params' + }, 'Params modified'); + + return Promise.resolve(data); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ + async: { + create (hook, next) { + hook.params.modified = 'params'; + + next(); + } + } + }); + + service.hooks({ + async: { + create (hook, next) { + hook.data.modified = 'second data'; + + next(); + } + } + }); + + return service.create({ some: 'thing' }); + }); + + it('chains multiple async hooks using array syntax', () => { + const dummyService = { + create (data, params) { + assert.deepStrictEqual(data, { + some: 'thing', + modified: 'second data' + }, 'Data modified'); + + assert.deepStrictEqual(params, { + modified: 'params' + }, 'Params modified'); + + return Promise.resolve(data); + } + }; + + const app = feathers().use('/dummy', dummyService); + const service = app.service('dummy'); + + service.hooks({ + async: { + create: [ + function (hook, next) { + hook.params.modified = 'params'; + + next(); + }, + function (hook, next) { + hook.data.modified = 'second data'; + + next(); + } + ] + } + }); + + return service.create({ some: 'thing' }); + }); + + it('.async hooks run in the correct order (#13)', () => { + const app = feathers().use('/dummy', { + get (id, params, callback) { + assert.deepStrictEqual(params.items, ['first', 'second', 'third']); + + return Promise.resolve({ + id: id, + items: [] + }); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + get (hook, next) { + hook.params.items = ['first']; + next(); + } + } + }); + + service.hooks({ + async: { + get: [ + function (hook, next) { + hook.params.items.push('second'); + next(); + }, + function (hook, next) { + hook.params.items.push('third'); + next(); + } + ] + } + }); + + return service.get(10); + }); + + it('async all hooks (#11)', () => { + const app = feathers().use('/dummy', { + get (id, params) { + assert.ok(params.asyncAllObject); + assert.ok(params.asyncAllMethodArray); + + return Promise.resolve({ + id: id, + items: [] + }); + }, + + find (params) { + assert.ok(params.asyncAllObject); + assert.ok(params.asyncAllMethodArray); + + return Promise.resolve([]); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + all (hook, next) { + hook.params.asyncAllObject = true; + next(); + } + } + }); + + service.hooks({ + async: [ + function (hook, next) { + hook.params.asyncAllMethodArray = true; + next(); + } + ] + }); + + return service.find(); + }); + + it('async hooks have service as context and keep it in service method (#17)', () => { + const app = feathers().use('/dummy', { + number: 42, + get (id, params) { + return Promise.resolve({ + id: id, + number: this.number, + test: params.test + }); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + get (hook, next) { + hook.params.test = this.number + 2; + return next(); + } + } + }); + + return service.get(10).then(data => { + assert.deepStrictEqual(data, { + id: 10, + number: 42, + test: 44 + }); + }); + }); + + it('calling next() multiple times does not do anything', () => { + const app = feathers().use('/dummy', { + get (id) { + return Promise.resolve({ id }); + } + }); + + const service = app.service('dummy'); + + service.hooks({ + async: { + get: [ + function (hook, next) { + return next(); + }, + + function (hook, next) { + next(); + return next(); + } + ] + } + }); + + return service.get(10).catch(e => { + assert.strictEqual(e.message, 'next() called multiple times'); + }); + }); + }); +}); diff --git a/packages/feathers/test/hooks/error.test.js b/packages/feathers/test/hooks/error.test.js index a9539fcf8b..b7afadf168 100644 --- a/packages/feathers/test/hooks/error.test.js +++ b/packages/feathers/test/hooks/error.test.js @@ -205,7 +205,7 @@ describe('`error` hooks', () => { } }).hooks({ error (hook) { - assert.strictEqual(hook.error.hook.type, 'before', + assert.strictEqual(hook.original.type, 'before', 'Original hook still set' ); assert.strictEqual(hook.id, 'dishes'); @@ -227,7 +227,7 @@ describe('`error` hooks', () => { }, error (hook) { - assert.strictEqual(hook.error.hook.type, 'after', + assert.strictEqual(hook.original.type, 'after', 'Original hook still set' ); assert.strictEqual(hook.id, 'dishes'); @@ -266,6 +266,27 @@ describe('`error` hooks', () => { }) .catch(error => assert.strictEqual(error.message, errorMessage)); }); + + it('error in async hook', () => { + service.hooks({ + async (hook) { + hook.modified = true; + + throw new Error(errorMessage); + }, + + error (hook) { + assert.ok(hook.modified); + assert.strictEqual(hook.original.type, 'before'); + } + }); + + return service.get('laundry') + .then(() => { + throw new Error('Should never get here'); + }) + .catch(error => assert.strictEqual(error.message, errorMessage)); + }); }); it('Error in before hook causes inter-service calls to have wrong hook context (https://github.com/feathersjs/feathers/issues/841)', () => { diff --git a/packages/feathers/test/hooks/hooks.test.js b/packages/feathers/test/hooks/hooks.test.js index efe184c633..e337e5c919 100644 --- a/packages/feathers/test/hooks/hooks.test.js +++ b/packages/feathers/test/hooks/hooks.test.js @@ -1,7 +1,78 @@ const assert = require('assert'); +const { hooks, HookContext } = require('@feathersjs/hooks'); const feathers = require('../../lib'); describe('hooks basics', () => { + it('mix @feathersjs/hooks and .hooks', async () => { + const svc = { + get (id, params) { + return Promise.resolve({ id, user: params.user }); + } + }; + + hooks(svc, { + get: [async (ctx, next) => { + ctx.chain.push('@hooks 1 before'); + await next(); + ctx.chain.push('@hooks 1 after'); + }] + }); + + const app = feathers().use('/dummy', svc); + const service = app.service('dummy'); + + service.hooks({ + before: { + get: ctx => { + ctx.chain.push('.hooks 1 before'); + } + }, + after: { + get: ctx => { + ctx.chain.push('.hooks 1 after'); + } + } + }); + + hooks(service, { + get: [async (ctx, next) => { + ctx.chain.push('@hooks 2 before'); + await next(); + ctx.chain.push('@hooks 2 after'); + }] + }); + + service.hooks({ + before: { + get: ctx => { + ctx.chain.push('.hooks 2 before'); + } + }, + after: { + get: ctx => { + ctx.chain.push('.hooks 2 after'); + } + } + }); + + const hookContext = new HookContext({ + chain: [] + }); + const resultContext = await service.get(1, {}, hookContext); + + assert.strictEqual(hookContext, resultContext); + assert.deepStrictEqual(resultContext.chain, [ + '@hooks 2 before', + '@hooks 1 before', + '.hooks 1 before', + '.hooks 2 before', + '.hooks 1 after', + '.hooks 2 after', + '@hooks 1 after', + '@hooks 2 after' + ]); + }); + it('validates arguments', () => { const app = feathers().use('/dummy', { get (id, params) { diff --git a/packages/feathers/test/hooks/with-hooks.test.js b/packages/feathers/test/hooks/with-hooks.test.js deleted file mode 100755 index a053709676..0000000000 --- a/packages/feathers/test/hooks/with-hooks.test.js +++ /dev/null @@ -1,129 +0,0 @@ - -const assert = require('assert'); -const feathers = require('../../lib'); -const { withHooks } = require('../../lib/hooks'); - -function createApp (findResult) { - return feathers() - .use('svc', { - create (data) { - return Promise.resolve(data); - }, - find () { - return Promise.resolve(findResult); - } - }); -} - -const testHook = hook => { - hook._called = 'called'; - return hook; -}; - -describe('services withHooks', () => { - it('get expected hook & object result', () => { - const data = { name: 'john' }; - const params = {}; - - const app = createApp(); - const svc = app.service('svc'); - - return withHooks({ - app, - service: svc, - method: 'create' - })({ - before: testHook - })(data, params, true) - .then(hook => { - assert.deepStrictEqual(hook.result, data, 'test result'); - assert.deepStrictEqual(hook, { - app, - params, - service: svc, - method: 'create', - path: 'svc', - data, - _called: 'called', - result: data, - type: 'after', - arguments: [ data, params ] - }, 'test hook'); - }); - }); - - it('get expected array result', () => { - const data = [{ name: 'john' }]; - - const app = createApp(); - const svc = app.service('svc'); - - return withHooks({ - app, - service: svc, - method: 'create' - })({ - before: testHook - })(data) - .then(result => { - assert.deepStrictEqual(result, data, 'test result'); - }); - }); - - it('get expected find result', () => { - const data = { total: 1, data: [{ name: 'john' }] }; - - const app = createApp(data); - const svc = app.service('svc'); - - return withHooks({ - app, - service: svc, - method: 'find' - })({ - before: testHook - })() - .then(result => { - assert.deepStrictEqual(result, data, 'test result'); - }); - }); - - it('get expected find result with no hooks at all', () => { - const data = { total: 1, data: [{ name: 'john' }] }; - - const app = createApp(data); - const svc = app.service('svc'); - - return withHooks({ - app, - service: svc, - method: 'find' - })()().then(result => { - assert.deepStrictEqual(result, data, 'test result'); - }); - }); - - it('test using keep hook', () => { - const data = [{ name: 'John', job: 'dev', address: { city: 'Montreal', postal: 'H4T 2A1' } }]; - - const app = createApp(); - const svc = app.service('svc'); - - return withHooks({ - app, - service: svc, - method: 'create' - })({ - after: context => { - (Array.isArray(context.result) ? context.result : [context.result]) - .forEach(value => { - delete value.job; - delete value.address.postal; - }); - } - })(data) - .then(result => { - assert.deepStrictEqual(result, [{ name: 'John', address: { city: 'Montreal' } }]); - }); - }); -}); diff --git a/packages/primus-client/.npmignore b/packages/primus-client/.npmignore deleted file mode 100644 index 65e3ba2eda..0000000000 --- a/packages/primus-client/.npmignore +++ /dev/null @@ -1 +0,0 @@ -test/ diff --git a/packages/primus-client/CHANGELOG.md b/packages/primus-client/CHANGELOG.md deleted file mode 100644 index bee9b6c2d6..0000000000 --- a/packages/primus-client/CHANGELOG.md +++ /dev/null @@ -1,343 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [4.5.2](https://github.com/feathersjs/feathers/compare/v4.5.1...v4.5.2) (2020-03-04) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.5.1](https://github.com/feathersjs/feathers/compare/v4.5.0...v4.5.1) (2020-01-24) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.5.0](https://github.com/feathersjs/feathers/compare/v4.4.3...v4.5.0) (2020-01-18) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.4.3](https://github.com/feathersjs/feathers/compare/v4.4.1...v4.4.3) (2019-12-06) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.4.1](https://github.com/feathersjs/feathers/compare/v4.4.0...v4.4.1) (2019-11-27) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.4.0](https://github.com/feathersjs/feathers/compare/v4.3.11...v4.4.0) (2019-11-27) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.11](https://github.com/feathersjs/feathers/compare/v4.3.10...v4.3.11) (2019-11-11) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.10](https://github.com/feathersjs/feathers/compare/v4.3.9...v4.3.10) (2019-10-26) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.9](https://github.com/feathersjs/feathers/compare/v4.3.8...v4.3.9) (2019-10-26) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.7](https://github.com/feathersjs/feathers/compare/v4.3.6...v4.3.7) (2019-10-14) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.5](https://github.com/feathersjs/feathers/compare/v4.3.4...v4.3.5) (2019-10-07) - - -### Bug Fixes - -* Change this reference in client libraries to explicitly passed app ([#1597](https://github.com/feathersjs/feathers/issues/1597)) ([4e4d10a](https://github.com/feathersjs/feathers/commit/4e4d10a)) - - - - - -## [4.3.4](https://github.com/feathersjs/feathers/compare/v4.3.3...v4.3.4) (2019-10-03) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.3](https://github.com/feathersjs/feathers/compare/v4.3.2...v4.3.3) (2019-09-21) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.2](https://github.com/feathersjs/feathers/compare/v4.3.1...v4.3.2) (2019-09-16) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -## [4.3.1](https://github.com/feathersjs/feathers/compare/v4.3.0...v4.3.1) (2019-09-09) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.3.0](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.4...v4.3.0) (2019-08-27) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.3.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.3...v4.3.0-pre.4) (2019-08-22) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.3.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.2...v4.3.0-pre.3) (2019-08-19) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.3.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.1...v4.3.0-pre.2) (2019-08-02) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.3.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.5...v4.3.0-pre.1) (2019-07-11) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.0.0-pre.5](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.4...v4.0.0-pre.5) (2019-07-10) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.0.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.3...v4.0.0-pre.4) (2019-07-05) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.0.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.2...v4.0.0-pre.3) (2019-06-01) - - -### Bug Fixes - -* Update dependencies and fix tests ([#1373](https://github.com/feathersjs/feathers/issues/1373)) ([d743a7f](https://github.com/feathersjs/feathers/commit/d743a7f)) - - - - - -# [4.0.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.1...v4.0.0-pre.2) (2019-05-15) - - -### Bug Fixes - -* Use `export =` in TypeScript definitions ([#1285](https://github.com/feathersjs/feathers/issues/1285)) ([12d0f4b](https://github.com/feathersjs/feathers/commit/12d0f4b)) - - - - - -# [4.0.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.0...v4.0.0-pre.1) (2019-05-08) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - -# [4.0.0-pre.0](https://github.com/feathersjs/feathers/compare/v3.2.0-pre.1...v4.0.0-pre.0) (2019-04-21) - - -### Bug Fixes - -* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803)) -* Update all dependencies to latest ([#1206](https://github.com/feathersjs/feathers/issues/1206)) ([e51e0f6](https://github.com/feathersjs/feathers/commit/e51e0f6)) - - -### Features - -* Add TypeScript definitions ([#1275](https://github.com/feathersjs/feathers/issues/1275)) ([9dd6713](https://github.com/feathersjs/feathers/commit/9dd6713)) - - - - - -## [1.1.7](https://github.com/feathersjs/feathers/compare/@feathersjs/primus-client@1.1.6...@feathersjs/primus-client@1.1.7) (2019-01-02) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - - -## [1.1.6](https://github.com/feathersjs/feathers/compare/@feathersjs/primus-client@1.1.5...@feathersjs/primus-client@1.1.6) (2018-12-16) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - - -## [1.1.5](https://github.com/feathersjs/feathers/compare/@feathersjs/primus-client@1.1.4...@feathersjs/primus-client@1.1.5) (2018-10-25) - - -### Bug Fixes - -* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803)) - - - - - - -## [1.1.4](https://github.com/feathersjs/feathers/compare/@feathersjs/primus-client@1.1.3...@feathersjs/primus-client@1.1.4) (2018-09-21) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - - -## [1.1.3](https://github.com/feathersjs/feathers/compare/@feathersjs/primus-client@1.1.2...@feathersjs/primus-client@1.1.3) (2018-09-17) - -**Note:** Version bump only for package @feathersjs/primus-client - - - - - - -## [1.1.2](https://github.com/feathersjs/feathers/compare/@feathersjs/primus-client@1.1.1...@feathersjs/primus-client@1.1.2) (2018-09-02) - -**Note:** Version bump only for package @feathersjs/primus-client - - -## 1.1.1 - -- Migrate to Monorepo ([feathers#462](https://github.com/feathersjs/feathers/issues/462)) - -## [v1.1.0](https://github.com/feathersjs/primus-client/tree/v1.1.0) (2018-02-09) -[Full Changelog](https://github.com/feathersjs/primus-client/compare/v1.0.3...v1.1.0) - -**Merged pull requests:** - -- Update @feathersjs/transport-commons to the latest version 🚀 [\#12](https://github.com/feathersjs/primus-client/pull/12) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v1.0.3](https://github.com/feathersjs/primus-client/tree/v1.0.3) (2018-02-05) -[Full Changelog](https://github.com/feathersjs/primus-client/compare/v1.0.2...v1.0.3) - -**Merged pull requests:** - -- Move to use transport-commons [\#11](https://github.com/feathersjs/primus-client/pull/11) ([daffl](https://github.com/daffl)) -- Update mocha to the latest version 🚀 [\#10](https://github.com/feathersjs/primus-client/pull/10) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update ws to the latest version 🚀 [\#9](https://github.com/feathersjs/primus-client/pull/9) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v1.0.2](https://github.com/feathersjs/primus-client/tree/v1.0.2) (2018-01-03) -[Full Changelog](https://github.com/feathersjs/primus-client/compare/v1.0.1...v1.0.2) - -**Merged pull requests:** - -- Update readme to correspond with latest release [\#8](https://github.com/feathersjs/primus-client/pull/8) ([daffl](https://github.com/daffl)) -- Update semistandard to the latest version 🚀 [\#7](https://github.com/feathersjs/primus-client/pull/7) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update feathers-memory to the latest version 🚀 [\#6](https://github.com/feathersjs/primus-client/pull/6) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update uws to the latest version 🚀 [\#5](https://github.com/feathersjs/primus-client/pull/5) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v1.0.1](https://github.com/feathersjs/primus-client/tree/v1.0.1) (2017-11-16) -[Full Changelog](https://github.com/feathersjs/primus-client/compare/v1.0.0...v1.0.1) - -**Merged pull requests:** - -- Add default export for better ES module \(TypeScript\) compatibility [\#4](https://github.com/feathersjs/primus-client/pull/4) ([daffl](https://github.com/daffl)) - -## [v1.0.0](https://github.com/feathersjs/primus-client/tree/v1.0.0) (2017-11-01) -[Full Changelog](https://github.com/feathersjs/primus-client/compare/v1.0.0-pre.2...v1.0.0) - -**Merged pull requests:** - -- Update dependencies for release [\#3](https://github.com/feathersjs/primus-client/pull/3) ([daffl](https://github.com/daffl)) - -## [v1.0.0-pre.2](https://github.com/feathersjs/primus-client/tree/v1.0.0-pre.2) (2017-10-23) -[Full Changelog](https://github.com/feathersjs/primus-client/compare/v1.0.0-pre.1...v1.0.0-pre.2) - -**Merged pull requests:** - -- Use scoped npm packages [\#2](https://github.com/feathersjs/primus-client/pull/2) ([daffl](https://github.com/daffl)) - -## [v1.0.0-pre.1](https://github.com/feathersjs/primus-client/tree/v1.0.0-pre.1) (2017-10-19) -**Merged pull requests:** - -- Update dependencies to enable Greenkeeper 🌴 [\#1](https://github.com/feathersjs/primus-client/pull/1) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - - - -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/packages/primus-client/LICENSE b/packages/primus-client/LICENSE deleted file mode 100644 index 7139cac0dd..0000000000 --- a/packages/primus-client/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2020 Feathers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/packages/primus-client/README.md b/packages/primus-client/README.md deleted file mode 100644 index 9c8ad9129e..0000000000 --- a/packages/primus-client/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# @feathersjs/primus-client - -[![CI](https://github.com/feathersjs/feathers/workflows/Node.js%20CI/badge.svg)](https://github.com/feathersjs/feathers/actions?query=workflow%3A%22Node.js+CI%22) -[![Dependency Status](https://img.shields.io/david/feathersjs/feathers.svg?style=flat-square&path=packages/primus-client)](https://david-dm.org/feathersjs/feathers?path=packages/primus-client) -[![Download Status](https://img.shields.io/npm/dm/@feathersjs/primus-client.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/primus-client) - -> Client services for Primus and feathers-primus - -## Installation - -``` -npm install @feathersjs/primus-client --save -``` - -## Documentation - -Refer to the [Feathers Primus client API documentation](https://docs.feathersjs.com/api/client/primus.html) for more details. - -## License - -Copyright (c) 2019 [Feathers contributors](https://github.com/feathersjs/client/graphs/contributors) - -Licensed under the [MIT license](LICENSE). diff --git a/packages/primus-client/index.d.ts b/packages/primus-client/index.d.ts deleted file mode 100644 index d394ee41ec..0000000000 --- a/packages/primus-client/index.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Primus removed its typings from the repo -// https://github.com/primus/primus/pull/623, as of 01/2018 there are none on -// DefinitelyTyped. - -declare const primusClient: FeathersPrimusClient; -export = primusClient; - -interface FeathersPrimusClient { - (socket: any, options?: primusClient.Options): () => void; - default: FeathersPrimusClient; -} - -declare namespace primusClient { - interface Options { - timeout?: number; - } -} diff --git a/packages/primus-client/lib/index.js b/packages/primus-client/lib/index.js deleted file mode 100644 index afd97f05b7..0000000000 --- a/packages/primus-client/lib/index.js +++ /dev/null @@ -1,32 +0,0 @@ -const Service = require('@feathersjs/transport-commons/client'); - -function primusClient (connection, options) { - if (!connection) { - throw new Error('Primus connection needs to be provided'); - } - - const defaultService = function (name) { - return new Service(Object.assign({}, options, { - name, - connection, - method: 'send' - })); - }; - - const initialize = function (app) { - if (typeof app.defaultService === 'function') { - throw new Error('Only one default client provider can be configured'); - } - - app.primus = connection; - app.defaultService = defaultService; - }; - - initialize.Service = Service; - initialize.service = defaultService; - - return initialize; -} - -module.exports = primusClient; -module.exports.default = primusClient; diff --git a/packages/primus-client/package.json b/packages/primus-client/package.json deleted file mode 100644 index e715045271..0000000000 --- a/packages/primus-client/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@feathersjs/primus-client", - "description": "Client services for Primus and feathers-primus", - "version": "4.5.2", - "homepage": "https://feathersjs.com", - "main": "lib/index.js", - "types": "index.d.ts", - "keywords": [ - "feathers", - "feathers-plugin" - ], - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/feathers" - }, - "repository": { - "type": "git", - "url": "git://github.com/feathersjs/feathers.git" - }, - "author": { - "name": "Feathers contributors", - "email": "hello@feathersjs.com", - "url": "https://feathersjs.com" - }, - "contributors": [], - "bugs": { - "url": "https://github.com/feathersjs/feathers/issues" - }, - "engines": { - "node": ">= 12" - }, - "scripts": { - "test": "mocha --config ../../.mocharc.json" - }, - "directories": { - "lib": "lib" - }, - "publishConfig": { - "access": "public" - }, - "dependencies": { - "@feathersjs/transport-commons": "^4.5.2" - }, - "devDependencies": { - "@feathersjs/commons": "^4.5.2", - "@feathersjs/feathers": "^4.5.2", - "@feathersjs/primus": "^4.5.2", - "@feathersjs/tests": "^4.5.2", - "feathers-memory": "^4.1.0", - "mocha": "^7.1.1", - "ws": "^7.2.3" - }, - "gitHead": "9b9f0f13387341bdd320f1e66feda828fca2c9f2" -} diff --git a/packages/primus-client/test/index.test.js b/packages/primus-client/test/index.test.js deleted file mode 100644 index 25e480ddf4..0000000000 --- a/packages/primus-client/test/index.test.js +++ /dev/null @@ -1,80 +0,0 @@ -const assert = require('assert'); -const feathers = require('@feathersjs/feathers'); -const baseTests = require('@feathersjs/tests/lib/client'); - -const server = require('./server'); -const primus = require('../lib'); - -describe('feathers-primus/client', () => { - let srv; - let socket; - - const app = feathers().configure(primus({}, { timeout: 500 })); - const service = app.service('todos'); - - before(done => { - srv = server(primus => { - service.connection = socket = new primus.Socket('http://localhost:12012'); - }).listen(12012); - - srv.on('listening', () => done()); - }); - - after(done => { - socket.socket.close(); - srv.close(done); - }); - - it('exports default', () => { - assert.strictEqual(primus.default, primus); - }); - - it('throws an error with no connection', () => { - try { - feathers().configure(primus()); - assert.ok(false); - } catch (e) { - assert.strictEqual(e.message, 'Primus connection needs to be provided'); - } - }); - - it('app has the primus attribute', () => { - assert.ok(app.primus); - }); - - it('throws an error when configured twice', () => { - try { - app.configure(primus({})); - assert.ok(false, 'Should never get here'); - } catch (e) { - assert.strictEqual(e.message, 'Only one default client provider can be configured'); - } - }); - - it('can initialize a client instance', () => { - const init = primus(service.connection); - const todos = init.service('todos'); - - assert.ok(todos instanceof init.Service, 'Returned service is a client'); - - return todos.find().then(todos => assert.deepEqual(todos, [ // eslint-disable-line - { - text: 'some todo', - complete: false, - id: 0 - } - ])); - }); - - it('times out with error when using non-existent service', () => { - const notMe = app.service('not-me'); - // Hack because we didn't set the connection at the beginning - notMe.connection = socket; - - return notMe.remove(1).catch(e => { - assert.strictEqual(e.message, `Service 'not-me' not found`); - }); - }); - - baseTests(service); -}); diff --git a/packages/primus-client/test/server.js b/packages/primus-client/test/server.js deleted file mode 100644 index 95247b9464..0000000000 --- a/packages/primus-client/test/server.js +++ /dev/null @@ -1,43 +0,0 @@ -const feathers = require('@feathersjs/feathers'); -const primus = require('@feathersjs/primus'); -const { Service } = require('feathers-memory'); - -// eslint-disable-next-line no-extend-native -Object.defineProperty(Error.prototype, 'toJSON', { - value: function () { - var alt = {}; - - Object.getOwnPropertyNames(this).forEach(function (key) { - alt[key] = this[key]; - }, this); - - return alt; - }, - configurable: true -}); - -class TodoService extends Service { - get (id, params) { - if (params.query.error) { - return Promise.reject(new Error('Something went wrong')); - } - - return super.get(id) - .then(data => Object.assign({ query: params.query }, data)); - } -} - -module.exports = function (cb) { - const app = feathers() - .configure(primus({ - transformer: 'websockets' - }, cb)) - .use('/todos', new TodoService()); - - app.service('todos').create({ - text: 'some todo', - complete: false - }); - - return app; -}; diff --git a/packages/primus/.npmignore b/packages/primus/.npmignore deleted file mode 100644 index 65e3ba2eda..0000000000 --- a/packages/primus/.npmignore +++ /dev/null @@ -1 +0,0 @@ -test/ diff --git a/packages/primus/CHANGELOG.md b/packages/primus/CHANGELOG.md deleted file mode 100644 index ace2b8dc04..0000000000 --- a/packages/primus/CHANGELOG.md +++ /dev/null @@ -1,519 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [4.5.2](https://github.com/feathersjs/feathers/compare/v4.5.1...v4.5.2) (2020-03-04) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.5.1](https://github.com/feathersjs/feathers/compare/v4.5.0...v4.5.1) (2020-01-24) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.5.0](https://github.com/feathersjs/feathers/compare/v4.4.3...v4.5.0) (2020-01-18) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.4.3](https://github.com/feathersjs/feathers/compare/v4.4.1...v4.4.3) (2019-12-06) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.4.1](https://github.com/feathersjs/feathers/compare/v4.4.0...v4.4.1) (2019-11-27) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.4.0](https://github.com/feathersjs/feathers/compare/v4.3.11...v4.4.0) (2019-11-27) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.11](https://github.com/feathersjs/feathers/compare/v4.3.10...v4.3.11) (2019-11-11) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.10](https://github.com/feathersjs/feathers/compare/v4.3.9...v4.3.10) (2019-10-26) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.9](https://github.com/feathersjs/feathers/compare/v4.3.8...v4.3.9) (2019-10-26) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.7](https://github.com/feathersjs/feathers/compare/v4.3.6...v4.3.7) (2019-10-14) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.5](https://github.com/feathersjs/feathers/compare/v4.3.4...v4.3.5) (2019-10-07) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.4](https://github.com/feathersjs/feathers/compare/v4.3.3...v4.3.4) (2019-10-03) - - -### Bug Fixes - -* Typing improvements ([#1580](https://github.com/feathersjs/feathers/issues/1580)) ([7818aec](https://github.com/feathersjs/feathers/commit/7818aec)) - - - - - -## [4.3.3](https://github.com/feathersjs/feathers/compare/v4.3.2...v4.3.3) (2019-09-21) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.2](https://github.com/feathersjs/feathers/compare/v4.3.1...v4.3.2) (2019-09-16) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -## [4.3.1](https://github.com/feathersjs/feathers/compare/v4.3.0...v4.3.1) (2019-09-09) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.3.0](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.4...v4.3.0) (2019-08-27) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.3.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.3...v4.3.0-pre.4) (2019-08-22) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.3.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.2...v4.3.0-pre.3) (2019-08-19) - - -### Bug Fixes - -* Use WeakMap to connect socket to connection ([#1509](https://github.com/feathersjs/feathers/issues/1509)) ([64807e3](https://github.com/feathersjs/feathers/commit/64807e3)) - - - - - -# [4.3.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.3.0-pre.1...v4.3.0-pre.2) (2019-08-02) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.3.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.5...v4.3.0-pre.1) (2019-07-11) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.0.0-pre.5](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.4...v4.0.0-pre.5) (2019-07-10) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.0.0-pre.4](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.3...v4.0.0-pre.4) (2019-07-05) - -**Note:** Version bump only for package @feathersjs/primus - - - - - -# [4.0.0-pre.3](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.2...v4.0.0-pre.3) (2019-06-01) - - -### Bug Fixes - -* Update dependencies and fix tests ([#1373](https://github.com/feathersjs/feathers/issues/1373)) ([d743a7f](https://github.com/feathersjs/feathers/commit/d743a7f)) - - - - - -# [4.0.0-pre.2](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.1...v4.0.0-pre.2) (2019-05-15) - - -### Bug Fixes - -* Use `export =` in TypeScript definitions ([#1285](https://github.com/feathersjs/feathers/issues/1285)) ([12d0f4b](https://github.com/feathersjs/feathers/commit/12d0f4b)) - - -### Features - -* Add global disconnect event ([#1355](https://github.com/feathersjs/feathers/issues/1355)) ([85afcca](https://github.com/feathersjs/feathers/commit/85afcca)) - - - - - -# [4.0.0-pre.1](https://github.com/feathersjs/feathers/compare/v4.0.0-pre.0...v4.0.0-pre.1) (2019-05-08) - - -### Features - -* Add params.headers to all transports when available ([#1303](https://github.com/feathersjs/feathers/issues/1303)) ([ebce79b](https://github.com/feathersjs/feathers/commit/ebce79b)) - - - - - -# [4.0.0-pre.0](https://github.com/feathersjs/feathers/compare/v3.2.0-pre.1...v4.0.0-pre.0) (2019-04-21) - - -### Bug Fixes - -* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803)) -* Update all dependencies to latest ([#1206](https://github.com/feathersjs/feathers/issues/1206)) ([e51e0f6](https://github.com/feathersjs/feathers/commit/e51e0f6)) -* **package:** update debug to version 3.0.0 ([#59](https://github.com/feathersjs/feathers/issues/59)) ([fedcf06](https://github.com/feathersjs/feathers/commit/fedcf06)) - - -### Features - -* Add TypeScript definitions ([#1275](https://github.com/feathersjs/feathers/issues/1275)) ([9dd6713](https://github.com/feathersjs/feathers/commit/9dd6713)) - - - - - -## [3.2.8](https://github.com/feathersjs/feathers/compare/@feathersjs/primus@3.2.7...@feathersjs/primus@3.2.8) (2019-01-02) - -**Note:** Version bump only for package @feathersjs/primus - - - - - - -## [3.2.7](https://github.com/feathersjs/feathers/compare/@feathersjs/primus@3.2.6...@feathersjs/primus@3.2.7) (2018-12-16) - -**Note:** Version bump only for package @feathersjs/primus - - - - - - -## [3.2.6](https://github.com/feathersjs/feathers/compare/@feathersjs/primus@3.2.5...@feathersjs/primus@3.2.6) (2018-10-25) - - -### Bug Fixes - -* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803)) - - - - - - -## [3.2.5](https://github.com/feathersjs/feathers/compare/@feathersjs/primus@3.2.4...@feathersjs/primus@3.2.5) (2018-09-21) - -**Note:** Version bump only for package @feathersjs/primus - - - - - - -## [3.2.4](https://github.com/feathersjs/feathers/compare/@feathersjs/primus@3.2.3...@feathersjs/primus@3.2.4) (2018-09-17) - -**Note:** Version bump only for package @feathersjs/primus - - - - - - -## [3.2.3](https://github.com/feathersjs/feathers/compare/@feathersjs/primus@3.2.2...@feathersjs/primus@3.2.3) (2018-09-02) - -**Note:** Version bump only for package @feathersjs/primus - - -## 3.2.2 - -- Migrate to Monorepo ([feathers#462](https://github.com/feathersjs/feathers/issues/462)) - -## [v3.2.1](https://github.com/feathersjs/primus/tree/v3.2.1) (2018-06-03) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.2.0...v3.2.1) - -**Merged pull requests:** - -- Update uberproto to the latest version 🚀 [\#80](https://github.com/feathersjs/primus/pull/80) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update uws to the latest version 🚀 [\#79](https://github.com/feathersjs/primus/pull/79) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update ws to the latest version 🚀 [\#78](https://github.com/feathersjs/primus/pull/78) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v3.2.0](https://github.com/feathersjs/primus/tree/v3.2.0) (2018-02-09) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.1.0...v3.2.0) - -**Merged pull requests:** - -- Update @feathersjs/transport-commons to the latest version 🚀 [\#77](https://github.com/feathersjs/primus/pull/77) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v3.1.0](https://github.com/feathersjs/primus/tree/v3.1.0) (2018-01-30) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.3...v3.1.0) - -**Merged pull requests:** - -- Update dependency to @feathersjs/transport-commons [\#76](https://github.com/feathersjs/primus/pull/76) ([daffl](https://github.com/daffl)) - -## [v3.0.3](https://github.com/feathersjs/primus/tree/v3.0.3) (2018-01-18) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.2...v3.0.3) - -**Merged pull requests:** - -- Remove setMaxListeners [\#75](https://github.com/feathersjs/primus/pull/75) ([daffl](https://github.com/daffl)) -- Update mocha to the latest version 🚀 [\#74](https://github.com/feathersjs/primus/pull/74) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update ws to the latest version 🚀 [\#73](https://github.com/feathersjs/primus/pull/73) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v3.0.2](https://github.com/feathersjs/primus/tree/v3.0.2) (2018-01-03) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.1...v3.0.2) - -**Merged pull requests:** - -- Update documentation to correspond with latest release [\#72](https://github.com/feathersjs/primus/pull/72) ([daffl](https://github.com/daffl)) -- Update semistandard to the latest version 🚀 [\#71](https://github.com/feathersjs/primus/pull/71) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update feathers-memory to the latest version 🚀 [\#70](https://github.com/feathersjs/primus/pull/70) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update uws to the latest version 🚀 [\#69](https://github.com/feathersjs/primus/pull/69) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v3.0.1](https://github.com/feathersjs/primus/tree/v3.0.1) (2017-11-16) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.0...v3.0.1) - -**Merged pull requests:** - -- Add default ES module default exports to make TypeScript integration … [\#68](https://github.com/feathersjs/primus/pull/68) ([daffl](https://github.com/daffl)) - -## [v3.0.0](https://github.com/feathersjs/primus/tree/v3.0.0) (2017-11-01) -[Full Changelog](https://github.com/feathersjs/primus/compare/v2.2.1...v3.0.0) - -**Merged pull requests:** - -- Update dependencies for release [\#67](https://github.com/feathersjs/primus/pull/67) ([daffl](https://github.com/daffl)) -- Throw an error when using an incompatible version of Feathers [\#66](https://github.com/feathersjs/primus/pull/66) ([daffl](https://github.com/daffl)) - -## [v2.2.1](https://github.com/feathersjs/primus/tree/v2.2.1) (2017-10-31) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.0-pre.4...v2.2.1) - -**Merged pull requests:** - -- Add an error when trying to use earlier versions with Feathers v3 [\#65](https://github.com/feathersjs/primus/pull/65) ([daffl](https://github.com/daffl)) - -## [v3.0.0-pre.4](https://github.com/feathersjs/primus/tree/v3.0.0-pre.4) (2017-10-25) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.0-pre.3...v3.0.0-pre.4) - -## [v3.0.0-pre.3](https://github.com/feathersjs/primus/tree/v3.0.0-pre.3) (2017-10-23) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.0-pre.2...v3.0.0-pre.3) - -**Merged pull requests:** - -- Updates for Feathers v3 \(Buzzard\) [\#64](https://github.com/feathersjs/primus/pull/64) ([daffl](https://github.com/daffl)) -- Update to use npm scopes [\#63](https://github.com/feathersjs/primus/pull/63) ([daffl](https://github.com/daffl)) - -## [v3.0.0-pre.2](https://github.com/feathersjs/primus/tree/v3.0.0-pre.2) (2017-10-19) -[Full Changelog](https://github.com/feathersjs/primus/compare/v3.0.0-pre.1...v3.0.0-pre.2) - -## [v3.0.0-pre.1](https://github.com/feathersjs/primus/tree/v3.0.0-pre.1) (2017-10-18) -[Full Changelog](https://github.com/feathersjs/primus/compare/v2.2.0...v3.0.0-pre.1) - -**Closed issues:** - -- An in-range update of babel-core is breaking the build 🚨 [\#56](https://github.com/feathersjs/primus/issues/56) -- Update npm version [\#55](https://github.com/feathersjs/primus/issues/55) - -**Merged pull requests:** - -- Compatibility and updates for Feathers v3 [\#62](https://github.com/feathersjs/primus/pull/62) ([daffl](https://github.com/daffl)) -- Update to new plugin infrastructure [\#61](https://github.com/feathersjs/primus/pull/61) ([daffl](https://github.com/daffl)) -- Update mocha to the latest version 🚀 [\#60](https://github.com/feathersjs/primus/pull/60) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update debug to the latest version 🚀 [\#59](https://github.com/feathersjs/primus/pull/59) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update primus type definition [\#58](https://github.com/feathersjs/primus/pull/58) ([Sieabah](https://github.com/Sieabah)) -- Update uws to the latest version 🚀 [\#57](https://github.com/feathersjs/primus/pull/57) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) - -## [v2.2.0](https://github.com/feathersjs/primus/tree/v2.2.0) (2017-05-29) -[Full Changelog](https://github.com/feathersjs/primus/compare/v2.1.0...v2.2.0) - -**Closed issues:** - -- An in-range update of feathers-hooks is breaking the build 🚨 [\#53](https://github.com/feathersjs/primus/issues/53) -- An in-range update of debug is breaking the build 🚨 [\#52](https://github.com/feathersjs/primus/issues/52) -- An in-range update of primus is breaking the build 🚨 [\#51](https://github.com/feathersjs/primus/issues/51) -- An in-range update of uws is breaking the build 🚨 [\#50](https://github.com/feathersjs/primus/issues/50) -- Expose headers and remote ip on feathers object [\#46](https://github.com/feathersjs/primus/issues/46) -- docs are out of date [\#28](https://github.com/feathersjs/primus/issues/28) - -**Merged pull requests:** - -- Update ws to the latest version 🚀 [\#54](https://github.com/feathersjs/primus/pull/54) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update semistandard to the latest version 🚀 [\#49](https://github.com/feathersjs/primus/pull/49) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update feathers-hooks to the latest version 🚀 [\#48](https://github.com/feathersjs/primus/pull/48) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update dependencies to enable Greenkeeper 🌴 [\#47](https://github.com/feathersjs/primus/pull/47) ([greenkeeper[bot]](https://github.com/apps/greenkeeper)) -- Update uws to version 0.14.0 🚀 [\#45](https://github.com/feathersjs/primus/pull/45) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - -## [v2.1.0](https://github.com/feathersjs/primus/tree/v2.1.0) (2017-03-01) -[Full Changelog](https://github.com/feathersjs/primus/compare/v2.0.0...v2.1.0) - -**Closed issues:** - -- Issues with using feathers-primus/client and feathers-client [\#41](https://github.com/feathersjs/primus/issues/41) -- Any breaking change in 2.0.0? [\#39](https://github.com/feathersjs/primus/issues/39) - -**Merged pull requests:** - -- Update uws to version 0.13.0 🚀 [\#44](https://github.com/feathersjs/primus/pull/44) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Typescript Definitions [\#43](https://github.com/feathersjs/primus/pull/43) ([AbraaoAlves](https://github.com/AbraaoAlves)) -- Update ws to version 2.0.0 🚀 [\#42](https://github.com/feathersjs/primus/pull/42) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update uws to version 0.12.0 🚀 [\#37](https://github.com/feathersjs/primus/pull/37) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - -## [v2.0.0](https://github.com/feathersjs/primus/tree/v2.0.0) (2016-12-02) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.4.1...v2.0.0) - -**Closed issues:** - -- uws [\#36](https://github.com/feathersjs/primus/issues/36) - -**Merged pull requests:** - -- Update feathers-memory to version 1.0.0 🚀 [\#35](https://github.com/feathersjs/primus/pull/35) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update feathers-commons to version 0.8.0 🚀 [\#34](https://github.com/feathersjs/primus/pull/34) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Swapping rm to rifraf and using relative path to \_mocha for windows support [\#33](https://github.com/feathersjs/primus/pull/33) ([corymsmith](https://github.com/corymsmith)) -- Update primus to version 6.0.5 🚀 [\#32](https://github.com/feathersjs/primus/pull/32) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- jshint —\> semistandard [\#30](https://github.com/feathersjs/primus/pull/30) ([corymsmith](https://github.com/corymsmith)) -- adding code coverage [\#29](https://github.com/feathersjs/primus/pull/29) ([ekryski](https://github.com/ekryski)) -- Update feathers-memory to version 0.8.0 🚀 [\#24](https://github.com/feathersjs/primus/pull/24) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update mocha to version 3.0.0 🚀 [\#22](https://github.com/feathersjs/primus/pull/22) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - -## [v1.4.1](https://github.com/feathersjs/primus/tree/v1.4.1) (2016-05-23) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.4.0...v1.4.1) - -**Merged pull requests:** - -- Update feathers-socket-commons to version 2.0.0 🚀 [\#18](https://github.com/feathersjs/primus/pull/18) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update babel-plugin-add-module-exports to version 0.2.0 🚀 [\#17](https://github.com/feathersjs/primus/pull/17) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - -## [v1.4.0](https://github.com/feathersjs/primus/tree/v1.4.0) (2016-04-28) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.3.3...v1.4.0) - -**Merged pull requests:** - -- Implement options for setting client timeout [\#16](https://github.com/feathersjs/primus/pull/16) ([daffl](https://github.com/daffl)) -- Update feathers-socket-commons to version 1.0.0 🚀 [\#15](https://github.com/feathersjs/primus/pull/15) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- More tests for error cases [\#14](https://github.com/feathersjs/primus/pull/14) ([daffl](https://github.com/daffl)) - -## [v1.3.3](https://github.com/feathersjs/primus/tree/v1.3.3) (2016-04-16) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.3.2...v1.3.3) - -**Merged pull requests:** - -- Increase the default number of maximum event listeners [\#13](https://github.com/feathersjs/primus/pull/13) ([daffl](https://github.com/daffl)) -- Update feathers-memory to version 0.7.0 🚀 [\#12](https://github.com/feathersjs/primus/pull/12) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update primus to version 5.0.1 🚀 [\#11](https://github.com/feathersjs/primus/pull/11) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update primus to version 5.0.0 🚀 [\#10](https://github.com/feathersjs/primus/pull/10) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - -## [v1.3.2](https://github.com/feathersjs/primus/tree/v1.3.2) (2016-02-11) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.3.1...v1.3.2) - -**Merged pull requests:** - -- Allow to instantiate a client instance [\#9](https://github.com/feathersjs/primus/pull/9) ([daffl](https://github.com/daffl)) - -## [v1.3.1](https://github.com/feathersjs/primus/tree/v1.3.1) (2016-02-09) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.3.0...v1.3.1) - -**Merged pull requests:** - -- Update feathers-commons to version 0.7.0 🚀 [\#8](https://github.com/feathersjs/primus/pull/8) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) - -## [v1.3.0](https://github.com/feathersjs/primus/tree/v1.3.0) (2016-02-09) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.2.1...v1.3.0) - -**Merged pull requests:** - -- Update feathers-memory to version 0.6.0 🚀 [\#6](https://github.com/feathersjs/primus/pull/6) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Update lodash to version 4.0.1 🚀 [\#5](https://github.com/feathersjs/primus/pull/5) ([greenkeeperio-bot](https://github.com/greenkeeperio-bot)) -- Adding nsp check [\#4](https://github.com/feathersjs/primus/pull/4) ([marshallswain](https://github.com/marshallswain)) - -## [v1.2.1](https://github.com/feathersjs/primus/tree/v1.2.1) (2016-01-21) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.2.0...v1.2.1) - -## [v1.2.0](https://github.com/feathersjs/primus/tree/v1.2.0) (2016-01-21) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.1.0...v1.2.0) - -**Merged pull requests:** - -- Refactoring to use feathers-socket-commons that support event filtering [\#3](https://github.com/feathersjs/primus/pull/3) ([daffl](https://github.com/daffl)) - -## [v1.1.0](https://github.com/feathersjs/primus/tree/v1.1.0) (2016-01-10) -[Full Changelog](https://github.com/feathersjs/primus/compare/v1.0.0...v1.1.0) - -**Merged pull requests:** - -- feathers-primus/client service and tests [\#1](https://github.com/feathersjs/primus/pull/1) ([daffl](https://github.com/daffl)) - -## [v1.0.0](https://github.com/feathersjs/primus/tree/v1.0.0) (2016-01-03) - - -\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* diff --git a/packages/primus/LICENSE b/packages/primus/LICENSE deleted file mode 100644 index 7139cac0dd..0000000000 --- a/packages/primus/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2020 Feathers - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/packages/primus/README.md b/packages/primus/README.md deleted file mode 100644 index a0a189f74e..0000000000 --- a/packages/primus/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# @feathersjs/primus - -[![CI](https://github.com/feathersjs/feathers/workflows/Node.js%20CI/badge.svg)](https://github.com/feathersjs/feathers/actions?query=workflow%3A%22Node.js+CI%22) -[![Dependency Status](https://img.shields.io/david/feathersjs/feathers.svg?style=flat-square&path=packages/primus)](https://david-dm.org/feathersjs/feathers?path=packages/primus) -[![Download Status](https://img.shields.io/npm/dm/@feathersjs/primus.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/primus) - -> The Feathers Primus real-time API provider - -## Installation - -``` -npm install @feathersjs/primus --save -``` - -## Documentation - -Refer to the [Feathers Primus API documentation](https://docs.feathersjs.com/api/primus.html) for more details. - -## License - -Copyright (c) 2019 [Feathers contributors](https://github.com/feathersjs/client/graphs/contributors) - -Licensed under the [MIT license](LICENSE). diff --git a/packages/primus/index.d.ts b/packages/primus/index.d.ts deleted file mode 100644 index 7226d5c3ea..0000000000 --- a/packages/primus/index.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Primus removed its typings from the repo -// https://github.com/primus/primus/pull/623, as of 01/2018 there are none on -// DefinitelyTyped. - -declare const configurePrimus: FeathersPrimus; -export = configurePrimus; - -interface FeathersPrimus { - (options?: any, callback?: (primus: any) => void): (app: any) => void; - default: FeathersPrimus; -} diff --git a/packages/primus/lib/index.js b/packages/primus/lib/index.js deleted file mode 100644 index 3ebc343185..0000000000 --- a/packages/primus/lib/index.js +++ /dev/null @@ -1,88 +0,0 @@ -const { socket: commons } = require('@feathersjs/transport-commons'); -const makeDebug = require('debug'); -const Proto = require('uberproto'); -const Primus = require('primus'); -const http = require('http'); -const Emitter = require('primus-emitter'); - -const debug = makeDebug('@feathersjs/primus'); - -function configurePrimus (config, configurer) { - return function (app) { - // Returns the connection object - const getParams = spark => spark.request.feathers; - // Mapping from connection back to its socket - const socketMap = new WeakMap(); - - if (!app.version || app.version < '3.0.0') { - throw new Error('@feathersjs/primus is not compatible with this version of Feathers. Use the latest at @feathersjs/feathers.'); - } - - const done = new Promise(resolve => { - Proto.mixin({ - listen (...args) { - if (typeof this._super === 'function') { - // If `listen` already exists - // usually the case when the app has been expressified - return this._super(...args); - } - - const server = http.createServer(); - - this.setup(server); - - return server.listen(...args); - }, - - setup (server) { - debug('Setting up Primus'); - - if (!this.primus) { - const primus = this.primus = new Primus(server, config); - - primus.plugin('emitter', Emitter); - - primus.use('feathers', function (req, res, next) { - req.feathers = { - headers: Object.keys(req.headers).reduce((result, key) => { - const value = req.headers[key]; - - if (typeof value !== 'object') { - result[key] = value; - } - - return result; - }, {}), - provider: 'primus' - }; - - next(); - }, 0); - - primus.on('connection', spark => socketMap.set(getParams(spark), spark)); - primus.on('disconnection', spark => app.emit('disconnect', getParams(spark))); - } - - if (typeof configurer === 'function') { - debug('Calling Primus configuration function'); - configurer.call(this, this.primus); - } - - resolve(this.primus); - - return this._super.apply(this, arguments); - } - }, app); - }); - - app.configure(commons({ - done, - socketMap, - getParams, - emit: 'send' - })); - }; -} - -module.exports = configurePrimus; -module.exports.default = configurePrimus; diff --git a/packages/primus/package.json b/packages/primus/package.json deleted file mode 100644 index 60e31d2c8f..0000000000 --- a/packages/primus/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@feathersjs/primus", - "description": "The Feathers Primus real-time API provider", - "version": "4.5.2", - "homepage": "https://feathersjs.com", - "main": "lib/", - "types": "index.d.ts", - "keywords": [ - "feathers", - "feathers-plugin" - ], - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/feathers" - }, - "repository": { - "type": "git", - "url": "git://github.com/feathersjs/feathers.git" - }, - "author": { - "name": "Feathers contributors", - "email": "hello@feathersjs.com", - "url": "https://feathersjs.com" - }, - "contributors": [], - "bugs": { - "url": "https://github.com/feathersjs/feathers/issues" - }, - "engines": { - "node": ">= 10" - }, - "scripts": { - "test": "mocha --config ../../.mocharc.json" - }, - "directories": { - "lib": "lib" - }, - "publishConfig": { - "access": "public" - }, - "dependencies": { - "@feathersjs/transport-commons": "^4.5.2", - "debug": "^4.1.1", - "primus": "^7.3.4", - "primus-emitter": "^3.1.1", - "uberproto": "^2.0.6" - }, - "devDependencies": { - "@feathersjs/commons": "^4.5.2", - "@feathersjs/express": "^4.5.2", - "@feathersjs/feathers": "^4.5.2", - "@feathersjs/tests": "^4.5.2", - "feathers-memory": "^4.1.0", - "lodash": "^4.17.15", - "mocha": "^7.1.1", - "ws": "^7.2.3" - }, - "gitHead": "9b9f0f13387341bdd320f1e66feda828fca2c9f2" -} diff --git a/packages/primus/test/events.js b/packages/primus/test/events.js deleted file mode 100644 index 784303a181..0000000000 --- a/packages/primus/test/events.js +++ /dev/null @@ -1,221 +0,0 @@ -const assert = require('assert'); -const { verify } = require('@feathersjs/tests/lib/fixture'); - -module.exports = function (name, options) { - const call = (method, ...args) => { - return new Promise((resolve, reject) => { - const { socket } = options; - const emitArgs = [ method, name ].concat(args); - - socket.send(...emitArgs, (error, result) => - error ? reject(error) : resolve(result) - ); - }); - }; - - const verifyEvent = (done, callback) => { - return function (data) { - try { - callback(data); - done(); - } catch (error) { - done(error); - } - }; - }; - - describe(`Basic ${name} service events`, () => { - let socket; - let connection; - - before(done => { - setTimeout(() => { - socket = new options.primus.Socket('http://localhost:7888'); - - options.app.once('connection', conn => { - connection = conn; - - options.app.channel('default').join(connection); - options.app.publish(() => options.app.channel('default')); - - done(); - }); - }, 250); - }); - - after(() => { - socket.socket.close(); - }); - - it(`${name} created`, done => { - let original = { - name: `created event` - }; - - socket.once(`${name} created`, verifyEvent(done, data => - verify.create(original, data) - )); - - call('create', original); - }); - - it(`${name} updated`, done => { - let original = { - name: `updated event` - }; - - socket.once(`${name} updated`, verifyEvent(done, data => - verify.update(10, original, data) - )); - - call('update', 10, original); - }); - - it(`${name} patched`, done => { - let original = { - name: `patched event` - }; - - socket.once(`${name} patched`, verifyEvent(done, data => - verify.patch(12, original, data) - )); - - call('patch', 12, original); - }); - - it(`${name} removed`, done => { - socket.once(`${name} removed`, verifyEvent(done, data => - verify.remove(333, data) - )); - - call('remove', 333); - }); - - it(`${name} custom events`, done => { - let service = options.app.service(name); - let original = { - name: `created event` - }; - let old = service.create; - - service.create = function (data) { - this.emit('log', { message: 'Custom log event', data }); - service.create = old; - return old.apply(this, arguments); - }; - - socket.once(`${name} log`, verifyEvent(done, data => { - // Primus does something that makes strict equal fail - assert.deepEqual(data, { // eslint-disable-line - message: `Custom log event`, data: original - }); - service.create = old; - })); - - call('create', original); - }); - }); - - describe('Event channels', () => { - const eventName = `${name} created`; - let connections; - let sockets; - - before(done => { - let counter = 0; - - connections = []; - sockets = []; - - options.app.on('connection', connection => { - if (connection.channel) { - counter++; - - options.app.channel(connection.channel).join(connection); - - connections.push(connection); - - if (counter === 3) { - done(); - } - } - }); - - sockets.push( - new options.primus.Socket('http://localhost:7888?channel=first'), - new options.primus.Socket('http://localhost:7888?channel=second'), - new options.primus.Socket('http://localhost:7888?channel=second') - ); - }); - - after(done => { - let counter = 0; - - sockets.forEach(socket => { - socket.once('close', () => { - if (++counter === sockets.length) { - done(); - } - }); - socket.socket.close(); - }); - }); - - it(`filters '${eventName}' event for a single channel`, done => { - const service = options.app.service(name); - const [ socket, otherSocket ] = sockets; - const onError = () => { - done(new Error('Should not get this event')); - }; - - service.publish('created', data => - options.app.channel(data.room) - ); - - socket.once(eventName, data => { - assert.strictEqual(data.room, 'first'); - otherSocket.removeListener(eventName, onError); - done(); - }); - - otherSocket.once(eventName, onError); - - service.create({ - text: 'Event dispatching test', - room: 'first' - }); - }); - - it(`filters '${name} created' event for a channel with multiple connections`, done => { - let counter = 0; - - const service = options.app.service(name); - const [ otherSocket, socketOne, socketTwo ] = sockets; - const onError = () => { - done(new Error('Should not get this event')); - }; - const onEvent = data => { - counter++; - assert.strictEqual(data.room, 'second'); - - if (++counter === 2) { - otherSocket.removeListener(eventName, onError); - done(); - } - }; - - service.publish('created', data => - options.app.channel(data.room) - ); - - socketOne.once(eventName, onEvent); - socketTwo.once(eventName, onEvent); - otherSocket.once(eventName, onError); - - service.create({ - text: 'Event dispatching test', - room: 'second' - }); - }); - }); -}; diff --git a/packages/primus/test/index.test.js b/packages/primus/test/index.test.js deleted file mode 100644 index e1b0b3b233..0000000000 --- a/packages/primus/test/index.test.js +++ /dev/null @@ -1,216 +0,0 @@ -const feathers = require('@feathersjs/feathers'); -const express = require('@feathersjs/express'); -const assert = require('assert'); -const request = require('request'); -const omit = require('lodash/omit'); -const extend = require('lodash/extend'); -const { Service } = require('@feathersjs/tests/lib/fixture'); - -const primus = require('../lib'); -const methodTests = require('./methods.js'); -const eventTests = require('./events'); - -describe('@feathersjs/primus', () => { - let options = { - socketParams: { - user: { name: 'David' }, - provider: 'primus' - } - }; - - before(done => { - const errorHook = function (hook) { - if (hook.params.query.hookError) { - throw new Error(`Error from ${hook.method}, ${hook.type} hook`); - } - }; - const app = options.app = feathers() - .configure(primus({ - transformer: 'websockets' - }, function (primus) { - primus.authorize(function (req, done) { - req.feathers.user = { name: 'David' }; - options.socketParams.headers = req.feathers.headers; - - const { channel } = req.query; - - if (channel) { - req.feathers.channel = channel; - } - - done(); - }); - - options.primus = primus; - options.socket = new primus.Socket('http://localhost:7888'); - })) - .use('todo', Service); - - app.service('todo').hooks({ - before: { get: errorHook } - }); - - options.server = app.listen(7888, function () { - app.use('tasks', Service); - app.service('tasks').hooks({ - before: { get: errorHook } - }); - done(); - }); - }); - - after(done => { - options.socket.socket.close(); - options.server.close(done); - }); - - it('exports default', () => { - assert.strictEqual(primus, primus.default); - }); - - it('is CommonJS compatible', () => { - assert.strictEqual(typeof require('../lib'), 'function'); - }); - - it('runs primus before setup (#131)', done => { - let counter = 0; - const app = feathers() - .configure(primus({ - transformer: 'websockets' - }, function () { - assert.strictEqual(counter, 0); - counter++; - })) - .use('/todos', { - find () { - return Promise.resolve([]); - }, - setup (app) { - assert.ok(app.primus); - assert.strictEqual(counter, 1, 'Primus configuration ran first'); - } - }); - - const srv = app.listen(9119); - srv.on('listening', () => srv.close(done)); - }); - - it('expressified app works', done => { - const data = { message: 'Hello world' }; - const app = express(feathers()) - .configure(primus({ - transformer: 'websockets' - })) - .use('/test', (req, res) => res.json(data)); - - const srv = app.listen(8993).on('listening', () => { - const url = 'http://localhost:8993/test'; - - request({ url, json: true }, (err, res) => { - assert.ok(!err); - assert.deepStrictEqual(res.body, data); - srv.close(done); - }); - }); - }); - - it('Passes handshake as service parameters.', function (done) { - const service = options.app.service('todo'); - const old = { - find: service.find, - create: service.create, - update: service.update, - remove: service.remove - }; - - service.find = function (params) { - assert.deepStrictEqual(omit(params, 'query', 'route', 'connection'), options.socketParams, - 'Handshake parameters passed on proper position'); - - return old.find.apply(this, arguments); - }; - - service.create = function (data, params) { - assert.deepStrictEqual(omit(params, 'query', 'route', 'connection'), options.socketParams, - 'Passed handshake parameters'); - - return old.create.apply(this, arguments); - }; - - service.update = function (id, data, params) { - assert.deepStrictEqual(params, extend({ - connection: options.socketParams, - route: {}, - query: { - test: 'param' - } - }, options.socketParams), 'Passed handshake parameters as query'); - - return old.update.apply(this, arguments); - }; - - options.socket.send('create', 'todo', {}, {}, error => { - assert.ok(!error); - - options.socket.send('update', 'todo', 1, {}, { test: 'param' }, () => { - assert.ok(!error); - extend(service, old); - done(); - }); - }); - }); - - it('Missing parameters in socket call works. (#88)', function (done) { - const service = options.app.service('todo'); - const old = { - find: service.find - }; - - service.find = function (params) { - assert.deepStrictEqual(omit(params, 'query', 'route', 'connection'), options.socketParams, - 'Handshake parameters passed on proper position'); - - return old.find.apply(this, arguments); - }; - - options.socket.send('find', 'todo', function () { - extend(service, old); - done(); - }); - }); - - it('connection and disconnect events (#1243, #1238)', (done) => { - const { app, primus } = options; - const mySocket = new primus.Socket('http://localhost:7888?channel=dctest'); - - app.on('connection', connection => { - if (connection.channel === 'dctest') { - assert.strictEqual(connection.channel, 'dctest'); - app.once('disconnect', disconnection => { - assert.strictEqual(disconnection.channel, 'dctest'); - done(); - }); - setTimeout(() => mySocket.end(), 100); - } - }); - - assert.ok(mySocket); - }); - - describe('Service method calls', () => { - describe('(\'method\', \'service\') event format', () => { - describe('Service', () => methodTests('todo', options)); - describe('Dynamic Service', () => methodTests('todo', options)); - }); - - describe('(\'service::method\') legacy event format', () => { - describe('Service', () => methodTests('tasks', options, true)); - describe('Dynamic Service', () => methodTests('tasks', options, true)); - }); - }); - - describe('Service events', () => { - describe('Service', () => eventTests('todo', options)); - describe('Dynamic Service', () => eventTests('tasks', options)); - }); -}); diff --git a/packages/primus/test/methods.js b/packages/primus/test/methods.js deleted file mode 100644 index ed7ab7828b..0000000000 --- a/packages/primus/test/methods.js +++ /dev/null @@ -1,113 +0,0 @@ -const assert = require('assert'); -const { verify } = require('@feathersjs/tests/lib/fixture'); - -module.exports = function (name, options, legacy = false) { - const call = (method, ...args) => - new Promise((resolve, reject) => { - const { socket } = options; - const prefix = legacy ? [ `${name}::${method}` ] - : [ method, name ]; - const emitArgs = prefix.concat(args); - - socket.send(...emitArgs, (error, result) => - error ? reject(error) : resolve(result) - ); - } - ); - - it(`invalid arguments cause an error`, () => - call('find', 1, {}).catch(e => - assert.strictEqual(e.message, 'Too many arguments for \'find\' method') - ) - ); - - it('.find', () => - call('find', {}).then(data => verify.find(data)) - ); - - it('.get', () => - call('get', 'laundry').then(data => verify.get('laundry', data)) - ); - - it('.get with error', () => - call('get', 'laundry', { error: true }) - .then(() => assert.ok(false, 'Should never get here')) - .catch(error => assert.strictEqual(error.message, 'Something for laundry went wrong')) - ); - - it('.get with runtime error', () => - call('get', 'laundry', { runtimeError: true }) - .then(() => assert.ok(false, 'Should never get here')) - .catch(error => assert.strictEqual(error.message, 'thingThatDoesNotExist is not defined')) - ); - - it('.get with error in hook', () => - call('get', 'laundry', { hookError: true }) - .then(() => assert.ok(false, 'Should never get here')) - .catch(error => assert.strictEqual(error.message, 'Error from get, before hook')) - ); - - it(`.create`, () => { - let original = { - name: `creating` - }; - - return call('create', original, {}) - .then(data => verify.create(original, data)); - }); - - it(`.create without parameters`, () => { - let original = { - name: `creating again` - }; - - return call('create', original) - .then(data => verify.create(original, data)); - }); - - it('.update', () => { - let original = { - name: 'updating' - }; - - return call('update', 23, original, {}) - .then(data => verify.update(23, original, data)); - }); - - it('.update many', () => { - const original = { - name: `updating`, - many: true - }; - - return call('update', null, original) - .then(data => verify.update(null, original, data)); - }); - - it('.patch', () => { - let original = { - name: `patching` - }; - - return call('patch', 25, original) - .then(data => verify.patch(25, original, data)); - }); - - it('.patch many', () => { - let original = { - name: `patching`, - many: true - }; - - return call('patch', null, original) - .then(data => verify.patch(null, original, data)); - }); - - it('.remove', () => - call('remove', 11).then(data => verify.remove(11, data)) - ); - - it('.remove many', () => - call('remove', null).then(data => verify.remove(null, data)) - ); -}; diff --git a/packages/rest-client/lib/index.js b/packages/rest-client/lib/index.js index bbc1a75458..e37e883b1e 100644 --- a/packages/rest-client/lib/index.js +++ b/packages/rest-client/lib/index.js @@ -1,6 +1,5 @@ const jQueryClient = require('./jquery'); const SuperagentClient = require('./superagent'); -const RequestClient = require('./request'); const FetchClient = require('./fetch'); const AxiosClient = require('./axios'); const AngularClient = require('./angular'); @@ -10,7 +9,6 @@ const AngularHttpClient = require('./angular-http-client'); const transports = { jquery: jQueryClient, superagent: SuperagentClient, - request: RequestClient, fetch: FetchClient, axios: AxiosClient, angular: AngularClient, @@ -54,5 +52,5 @@ function restClient (base = '') { return result; } -module.exports = Object.assign(restClient, { SuperagentClient, FetchClient, jQueryClient, RequestClient, AxiosClient, AngularClient, AngularHttpClient }); +module.exports = Object.assign(restClient, { SuperagentClient, FetchClient, jQueryClient, AxiosClient, AngularClient, AngularHttpClient }); module.exports.default = restClient; diff --git a/packages/rest-client/lib/request.js b/packages/rest-client/lib/request.js deleted file mode 100644 index 7f9497c723..0000000000 --- a/packages/rest-client/lib/request.js +++ /dev/null @@ -1,32 +0,0 @@ -const Base = require('./base'); - -class RequestService extends Base { - request (options, params) { - return new Promise((resolve, reject) => { - const { connection = {} } = params; - const headers = Object.assign({}, options.headers, connection.headers); - - this.connection(Object.assign({ - json: true - }, options, params.connection, { headers }), function (error, res, data) { - if (error) { - return reject(error); - } - - if (!error && res.statusCode >= 400) { - if (typeof data === 'string') { - return reject(new Error(data)); - } - - data.response = res; - - return reject(Object.assign(new Error(data.message), data)); - } - - resolve(data); - }); - }); - } -} - -module.exports = RequestService; diff --git a/packages/rest-client/package.json b/packages/rest-client/package.json index ddad12e82f..c2089f26ed 100644 --- a/packages/rest-client/package.json +++ b/packages/rest-client/package.json @@ -57,7 +57,6 @@ "feathers-memory": "^4.1.0", "mocha": "^7.1.1", "node-fetch": "^2.6.0", - "request": "^2.88.2", "rxjs": "^6.5.5", "shx": "^0.3.2", "superagent": "^5.2.2" diff --git a/packages/rest-client/test/index.test.js b/packages/rest-client/test/index.test.js index e1f6f00e1c..484a3cb8fe 100644 --- a/packages/rest-client/test/index.test.js +++ b/packages/rest-client/test/index.test.js @@ -12,7 +12,6 @@ describe('REST client tests', function () { assert.strictEqual(typeof init, 'function'); assert.strictEqual(typeof transports.jquery, 'function'); - assert.strictEqual(typeof transports.request, 'function'); assert.strictEqual(typeof transports.superagent, 'function'); assert.strictEqual(typeof transports.fetch, 'function'); }); diff --git a/packages/rest-client/test/request.test.js b/packages/rest-client/test/request.test.js deleted file mode 100644 index 3c93c4c072..0000000000 --- a/packages/rest-client/test/request.test.js +++ /dev/null @@ -1,115 +0,0 @@ -const assert = require('assert'); -const request = require('request'); -const feathers = require('@feathersjs/feathers'); -const baseTests = require('@feathersjs/tests/lib/client'); -const errors = require('@feathersjs/errors'); -const server = require('./server'); -const rest = require('../lib/index'); - -describe('node-request REST connector', function () { - const url = 'http://localhost:6777'; - const setup = rest(url).request(request); - const app = feathers().configure(setup); - const service = app.service('todos'); - - before(function (done) { - this.server = server().listen(6777, done); - }); - - after(function (done) { - this.server.close(done); - }); - - baseTests(service); - - it('supports custom headers', () => { - const headers = { - 'Authorization': 'let-me-in' - }; - - return service.get(0, { headers }).then(todo => - assert.deepStrictEqual(todo, { - id: 0, - authorization: 'let-me-in', - text: 'some todo', - complete: false, - query: {} - }) - ); - }); - - it('supports params.connection', () => { - const connection = { - headers: { - 'Authorization': 'let-me-in' - } - }; - - return service.get(0, { connection }).then(todo => - assert.deepStrictEqual(todo, { - id: 0, - authorization: 'let-me-in', - text: 'some todo', - complete: false, - query: {} - }) - ); - }); - - it('can initialize a client instance', () => { - const init = rest(url).request(request); - const todos = init.service('todos'); - - assert.ok(todos instanceof init.Service, 'Returned service is a client'); - - return todos.find({}).then(todos => - assert.deepStrictEqual(todos, [ - { - text: 'some todo', - complete: false, - id: 0 - } - ]) - ); - }); - - it('catches connection errors', () => { - const init = rest('nowhere').request(request); - const todos = init.service('todos'); - - return todos.find().catch(error => - assert.strictEqual(error.message, 'Invalid URI "nowhere/todos"') - ); - }); - - it('supports nested arrays in queries', () => { - const query = { test: { $in: [ '0', '1', '2' ] } }; - - return service.get(0, { query }).then(data => - assert.deepStrictEqual(data.query, query) - ); - }); - - it('converts errors properly', () => { - return service.get(1, { query: { error: true } }).catch(e => - assert.strictEqual(e.message, 'Something went wrong') - ); - }); - - it('remove many', () => { - return service.remove(null).then(todo => { - assert.strictEqual(todo.id, null); - assert.strictEqual(todo.text, 'deleted many'); - }); - }); - - it('converts feathers errors (#50)', () => { - return service.get(0, { query: { feathersError: true } }).catch(error => { - assert.ok(error instanceof errors.NotAcceptable); - assert.strictEqual(error.message, 'This is a Feathers error'); - assert.strictEqual(error.code, 406); - assert.deepStrictEqual(error.data, { data: true }); - assert.ok(error.response); - }); - }); -}); diff --git a/packages/socketio/test/index.test.js b/packages/socketio/test/index.test.js index 98a4983735..bc52a57d37 100644 --- a/packages/socketio/test/index.test.js +++ b/packages/socketio/test/index.test.js @@ -4,7 +4,7 @@ const assert = require('assert'); const omit = require('lodash/omit'); const extend = require('lodash/extend'); const io = require('socket.io-client'); -const request = require('request'); +const axios = require('axios'); const { Service } = require('@feathersjs/tests/lib/fixture'); const methodTests = require('./methods.js'); @@ -108,21 +108,20 @@ describe('@feathersjs/socketio', () => { const app = express(feathers()) .configure(socketio()) .use('/test', (req, res) => res.json(data)); - const srv = app.listen(8992).on('listening', () => { - const url = 'http://localhost:8992/socket.io/socket.io.js'; - - request(url, (err, res) => { - assert.ok(!err); - assert.strictEqual(res.statusCode, 200); + + const srv = app.listen(8992).on('listening', async () => { + const response = await axios({ + url: 'http://localhost:8992/socket.io/socket.io.js' + }); - const url = 'http://localhost:8992/test'; + assert.strictEqual(response.status, 200); - request({ url, json: true }, (err, res) => { - assert.ok(!err); - assert.deepStrictEqual(res.body, data); - srv.close(done); - }); + const res = await axios({ + url: 'http://localhost:8992/test' }); + + assert.deepStrictEqual(res.data, data); + srv.close(done); }); }); @@ -131,14 +130,11 @@ describe('@feathersjs/socketio', () => { path: '/test/' }, ioInstance => assert.ok(ioInstance))); - let srv = application.listen(8987).on('listening', () => { - const url = 'http://localhost:8987/test/socket.io.js'; + let srv = application.listen(8987).on('listening', async () => { + const { status } = await axios('http://localhost:8987/test/socket.io.js'); - // eslint-disable-next-line handle-callback-err - request(url, (err, res) => { - assert.strictEqual(res.statusCode, 200); - srv.close(done); - }); + assert.strictEqual(status, 200); + srv.close(done); }); }); diff --git a/packages/transport-commons/src/channels/index.ts b/packages/transport-commons/src/channels/index.ts index 2ef89d880c..1fcc5db9ac 100644 --- a/packages/transport-commons/src/channels/index.ts +++ b/packages/transport-commons/src/channels/index.ts @@ -93,7 +93,7 @@ export function channels () { return; } - const results = Array.isArray(result) ? compact(flattenDeep(result)) : [result]; + const results = (Array.isArray(result) ? compact(flattenDeep(result)) : [result] as Channel[]); const channel = new CombinedChannel(results); if (channel && channel.length > 0) { diff --git a/packages/transport-commons/src/socket/utils.ts b/packages/transport-commons/src/socket/utils.ts index fbf9a5c0d8..524806ccbf 100644 --- a/packages/transport-commons/src/socket/utils.ts +++ b/packages/transport-commons/src/socket/utils.ts @@ -1,6 +1,6 @@ import Debug from 'debug'; import isEqual from 'lodash/isEqual'; -import errors from '@feathersjs/errors'; +import { NotFound, MethodNotAllowed } from '@feathersjs/errors'; import { HookContext, Application } from '@feathersjs/feathers'; import { CombinedChannel } from '../channels/channel/combined'; import { RealTimeConnection } from '../channels/channel/base'; @@ -81,7 +81,7 @@ export function runMethod (app: Application, connection: RealTimeConnection, pat // No valid service was found, return a 404 // just like a REST route would if (lookup === null) { - return Promise.reject(new errors.NotFound(`Service '${path}' not found`)); + return Promise.reject(new NotFound(`Service '${path}' not found`)); } const { service, params: route = {} } = lookup; @@ -89,7 +89,7 @@ export function runMethod (app: Application, connection: RealTimeConnection, pat // Only service methods are allowed // @ts-ignore if (paramsPositions[method] === undefined || typeof service[method] !== 'function') { - return Promise.reject(new errors.MethodNotAllowed(`Method '${method}' not allowed on service '${path}'`)); + return Promise.reject(new MethodNotAllowed(`Method '${method}' not allowed on service '${path}'`)); } const position = paramsPositions[method]; diff --git a/packages/transport-commons/test/client.test.ts b/packages/transport-commons/test/client.test.ts index a5d11af48f..067151e765 100644 --- a/packages/transport-commons/test/client.test.ts +++ b/packages/transport-commons/test/client.test.ts @@ -1,6 +1,6 @@ import assert from 'assert'; import { EventEmitter } from 'events'; -import errors from '@feathersjs/errors'; +import { NotAuthenticated } from '@feathersjs/errors'; import { Service } from '../src/client'; declare type DummyCallback = (err: any, data?: any) => void; @@ -143,11 +143,11 @@ describe('client', () => { it('converts to feathers-errors (#19)', () => { connection.once('create', (_path: any, _data: any, _params: any, callback: DummyCallback) => - callback(new errors.NotAuthenticated('Test', { hi: 'me' }).toJSON()) + callback(new NotAuthenticated('Test', { hi: 'me' }).toJSON()) ); return service.create(testData).catch(error => { - assert.ok(error instanceof errors.NotAuthenticated); + assert.ok(error instanceof NotAuthenticated); assert.strictEqual(error.name, 'NotAuthenticated'); assert.strictEqual(error.message, 'Test'); assert.strictEqual(error.code, 401); diff --git a/packages/transport-commons/test/socket/utils.test.ts b/packages/transport-commons/test/socket/utils.test.ts index b2bc57d3aa..34e231dc4b 100644 --- a/packages/transport-commons/test/socket/utils.test.ts +++ b/packages/transport-commons/test/socket/utils.test.ts @@ -1,7 +1,7 @@ import assert from 'assert'; import { EventEmitter } from 'events'; import feathers, { Application, Params } from '@feathersjs/feathers'; -import errors from '@feathersjs/errors'; +import { NotAuthenticated } from '@feathersjs/errors'; import { routing } from '../../src/routing'; import { @@ -191,7 +191,7 @@ describe('socket commons utils', () => { app.use('/myservice', { get (id: number|string, params: Params) { if (params.query.error) { - return Promise.reject(new errors.NotAuthenticated('None shall pass')); + return Promise.reject(new NotAuthenticated('None shall pass')); } return Promise.resolve({ id }); } @@ -292,9 +292,7 @@ describe('socket commons utils', () => { assert.deepStrictEqual(error, { name: 'NotFound', message: 'Service \'ohmyservice\' not found', code: 404, - className: 'not-found', - data: undefined, - errors: {} + className: 'not-found' }); done(); } catch (e) { @@ -312,9 +310,7 @@ describe('socket commons utils', () => { name: 'MethodNotAllowed', message: 'Method \'create\' not allowed on service \'myservice\'', code: 405, - className: 'method-not-allowed', - data: undefined, - errors: {} + className: 'method-not-allowed' }); done(); } catch (e) { @@ -332,9 +328,7 @@ describe('socket commons utils', () => { name: 'MethodNotAllowed', message: 'Method \'blabla\' not allowed on service \'myservice\'', code: 405, - className: 'method-not-allowed', - data: undefined, - errors: {} + className: 'method-not-allowed' }); done(); } catch (e) { @@ -351,10 +345,8 @@ describe('socket commons utils', () => { assert.deepStrictEqual(error, { name: 'NotAuthenticated', message: 'None shall pass', - data: undefined, code: 401, - className: 'not-authenticated', - errors: {} + className: 'not-authenticated' }); done(); } catch (e) { diff --git a/tslint.json b/tslint.json index 462ca5bfbf..36defa6ace 100644 --- a/tslint.json +++ b/tslint.json @@ -11,6 +11,7 @@ "packages/authentication-oauth/lib/**", "packages/configuration/lib/**", "packages/commons/lib/**", + "packages/errors/lib/**", "packages/transport-commons/lib/**", "packages/tests/lib/**", "**/node_modules/**",