Skip to content

Commit

Permalink
feat(core): Remove Uberproto (#2178)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Services no longer extend Uberproto objects and
`service.mixin()` is no longer available.
  • Loading branch information
daffl committed Jan 5, 2021
1 parent 6d18065 commit ddf8821
Show file tree
Hide file tree
Showing 13 changed files with 143 additions and 280 deletions.
280 changes: 82 additions & 198 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/adapter-tests/src/methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ export default (test: any, app: any, _errors: any, serviceName: string, idProp:
let throwing: any;

before(() => {
throwing = app.service(serviceName).extend({
throwing = Object.assign(Object.create(app.service(serviceName)), {
get store () {
return app.service(serviceName).store;
},
Expand Down
3 changes: 1 addition & 2 deletions packages/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@
"@types/express": "^4.17.9",
"debug": "^4.3.1",
"express": "^4.17.1",
"lodash": "^4.17.20",
"uberproto": "^2.0.6"
"lodash": "^4.17.20"
},
"devDependencies": {
"@feathersjs/authentication": "^5.0.0-pre.1",
Expand Down
13 changes: 6 additions & 7 deletions packages/express/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-ignore
import Proto from 'uberproto';
import express, { Express, static as _static, json, raw, text, urlencoded, query } from 'express';
import Debug from 'debug';
import {
Expand Down Expand Up @@ -55,9 +53,10 @@ export default function feathersExpress<T = any> (feathersApp?: FeathersApplicat
throw new Error(`@feathersjs/express requires an instance of a Feathers application version 3.x or later (got ${feathersApp.version || 'unknown'})`);
}

const { use, listen } = expressApp as any;
// An Uberproto mixin that provides the extended functionality
const mixin: any = {
use (location: string) {
use (location: string, ...rest: any[]) {
let service: any;
const middleware = Array.from(arguments).slice(1)
.reduce(function (middleware, arg) {
Expand All @@ -81,7 +80,7 @@ export default function feathersExpress<T = any> (feathersApp?: FeathersApplicat
// Check for service (any object with at least one service method)
if (hasMethod(['handle', 'set']) || !hasMethod(this.methods.concat('setup'))) {
debug('Passing app.use call to Express app');
return this._super.apply(this, arguments);
return use.call(this, location, ...rest);
}

debug('Registering service with middleware', middleware);
Expand All @@ -91,8 +90,8 @@ export default function feathersExpress<T = any> (feathersApp?: FeathersApplicat
return this;
},

listen () {
const server = this._super.apply(this, arguments);
listen (...args: any[]) {
const server = listen.call(this, ...args);

this.setup(server);
debug('Feathers application listening');
Expand All @@ -112,7 +111,7 @@ export default function feathersExpress<T = any> (feathersApp?: FeathersApplicat
}
});

return Proto.mixin(mixin, expressApp);
return Object.assign(expressApp, mixin);
}

if (typeof module !== 'undefined') {
Expand Down
14 changes: 4 additions & 10 deletions packages/express/src/rest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,10 @@ const HTTP_METHOD = Symbol('@feathersjs/express/rest/HTTP_METHOD');

export function httpMethod (verb: any, uris?: any) {
return (method: any) => {
Object.defineProperty(method, HTTP_METHOD, {
enumerable: false,
configurable: true,
writable: false,
value: (Array.isArray(uris) ? uris : [uris])
.reduce(
(result, uri) => ([...result, { verb, uri }]),
method[HTTP_METHOD] || []
)
});
method[HTTP_METHOD] = (Array.isArray(uris) ? uris : [uris]).reduce(
(result, uri) => ([...result, { verb, uri }]),
method[HTTP_METHOD] || []
);

return method;
};
Expand Down
6 changes: 3 additions & 3 deletions packages/feathers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"version": "npm run write-version",
"publish": "npm run reset-version",
"compile": "shx rm -rf lib/ && tsc",
"test": "npm run compile && mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
"test": "npm run compile && npm run mocha",
"mocha": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
},
"engines": {
"node": ">= 12"
Expand All @@ -59,8 +60,7 @@
"@feathersjs/commons": "^5.0.0-pre.1",
"@feathersjs/hooks": "^0.6.1",
"debug": "^4.3.1",
"events": "^3.2.0",
"uberproto": "^2.0.6"
"events": "^3.2.0"
},
"devDependencies": {
"@types/mocha": "^8.2.0",
Expand Down
11 changes: 3 additions & 8 deletions packages/feathers/src/application.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import Debug from 'debug';
import { stripSlashes } from '@feathersjs/commons';

// @ts-ignore
import Uberproto from 'uberproto';
import events from './events';
import hooks from './hooks';
import version from './version';
import { BaseApplication, Service } from './declarations';

const debug = Debug('feathers:application');

const Proto = Uberproto.extend({
create: null
});

interface AppExtensions {
_isSetup: boolean;
init (): void;
Expand Down Expand Up @@ -106,8 +100,9 @@ export default {
throw new Error(`Invalid service object passed for path \`${location}\``);
}

// If the service is already Uberproto'd use it directly
const protoService = Proto.isPrototypeOf(service) ? service : Proto.extend(service);
// Use existing service or create a new object with prototype pointing to original
const isFeathersService = typeof service.hooks === 'function' && (service as any)._serviceEvents;
const protoService = isFeathersService ? service : Object.create(service);

debug(`Registering new service at \`${location}\``);

Expand Down
10 changes: 4 additions & 6 deletions packages/feathers/src/events.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-ignore
import Proto from 'uberproto';
import { EventEmitter } from 'events';
import { HookContext, Service, Application } from './declarations';

Expand Down Expand Up @@ -32,9 +30,9 @@ export function eventMixin (this: Application, service: Service<any>) {
const isEmitter = typeof service.on === 'function' &&
typeof service.emit === 'function';

// If not, mix it in (the service is always an Uberproto object that has a .mixin)
if (typeof service.mixin === 'function' && !isEmitter) {
service.mixin(EventEmitter.prototype);
// If not, add EventEmitter functionality
if (!isEmitter) {
Object.assign(service, EventEmitter.prototype);
}

// Define non-enumerable properties of
Expand Down Expand Up @@ -81,7 +79,7 @@ export default function () {
app.hooks({ finally: eventHook() });

// Make the app an event emitter
Proto.mixin(EventEmitter.prototype, app);
Object.assign(app, EventEmitter.prototype);

app.mixins.push(eventMixin);
};
Expand Down
48 changes: 26 additions & 22 deletions packages/feathers/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,29 @@ function withHooks (app: Application, service: Service<any>, methods: string[])
hooksDecorator(service, hookMap);
}

function mixinMethod (this: any) {
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 : this._super.createContext();

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;
}
});
const mixinMethod = (_super: any) => {
const result = function (this: any) {
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 : _super.createContext();

return _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;
}
});
};

return Object.assign(result, _super);
}

// A service mixin that adds `service.hooks()` method and functionality
Expand All @@ -116,7 +120,7 @@ const hookMixin = exports.hookMixin = function hookMixin (service: any) {
return;
}

service.methods = Object.getOwnPropertyNames(service)
service.methods = Object.getOwnPropertyNames(Object.getPrototypeOf(service))
.filter(key => typeof service[key] === 'function' && service[key][ACTIVATE_HOOKS])
.reduce((result, methodName) => {
result[methodName] = service[methodName][ACTIVATE_HOOKS];
Expand All @@ -143,15 +147,15 @@ const hookMixin = exports.hookMixin = function hookMixin (service: any) {
return mixin;
}

mixin[method] = mixinMethod;
mixin[method] = mixinMethod(service[method]);

return mixin;
}, {} as any);

// Add .hooks method and properties to the service
enableHooks(service, methodNames, app.hookTypes);

service.mixin(mixin);
Object.assign(service, mixin);
};

export default function () {
Expand Down
11 changes: 2 additions & 9 deletions packages/feathers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
// @ts-ignore
import Proto from 'uberproto';
import Application from './application';
import version from './version';
import { Application as ApplicationType } from './declarations'

const baseObject = Object.create(null);

export default function feathers<ServiceTypes = {}> (): ApplicationType<ServiceTypes> {
const app = Object.create(baseObject);

// Mix in the base application
Proto.mixin(Application, app);
const app = Object.assign({}, Application);

app.init();

return app;
return app as any as ApplicationType<ServiceTypes>;
}

export { version };
Expand Down
4 changes: 1 addition & 3 deletions packages/feathers/test/application.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-ignore
import Proto from 'uberproto';
import assert from 'assert';
import feathers, { Id, version } from '../src'
import { HookContext } from '@feathersjs/hooks';
Expand Down Expand Up @@ -117,7 +115,7 @@ describe('Feathers application', () => {
const app = feathers().use('/dummy', dummyService);
const wrappedService = app.service('dummy');

assert.ok(Proto.isPrototypeOf(wrappedService), 'Service got wrapped as Uberproto object');
assert.strictEqual(Object.getPrototypeOf(wrappedService), dummyService, 'Object points to original service prototype');

return wrappedService.create({
message: 'Test message'
Expand Down
3 changes: 1 addition & 2 deletions packages/socketio/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@
"dependencies": {
"@feathersjs/transport-commons": "^5.0.0-pre.1",
"debug": "^4.3.1",
"socket.io": "^3.0.4",
"uberproto": "^2.0.6"
"socket.io": "^3.0.4"
},
"devDependencies": {
"@feathersjs/adapter-memory": "^5.0.0-pre.1",
Expand Down
18 changes: 9 additions & 9 deletions packages/socketio/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-ignore
import Proto from 'uberproto';
import Debug from 'debug';
import { Server, ServerOptions } from 'socket.io';
import http from 'http';
Expand Down Expand Up @@ -38,12 +36,14 @@ function configureSocketio (port?: any, options?: any, config?: any) {
// Promise that resolves with the Socket.io `io` instance
// when `setup` has been called (with a server)
const done = new Promise(resolve => {
Proto.mixin({
listen (...args: any[]) {
if (typeof this._super === 'function') {
const { listen, setup } = app as any;

Object.assign(app, {
listen (this: any, ...args: any[]) {
if (typeof listen === 'function') {
// If `listen` already exists
// usually the case when the app has been expressified
return this._super(...args);
return listen.call(this, ...args);
}

const server = http.createServer();
Expand All @@ -53,7 +53,7 @@ function configureSocketio (port?: any, options?: any, config?: any) {
return server.listen(...args);
},

setup (server: http.Server) {
setup (this: any, server: http.Server, ...rest: any[]) {
if (!this.io) {
const io = this.io = new Server(port || server, options);

Expand All @@ -74,9 +74,9 @@ function configureSocketio (port?: any, options?: any, config?: any) {

resolve(this.io);

return this._super.apply(this, arguments);
return setup.call(this, server, ...rest);
}
}, app);
});
});

app.configure(socket({
Expand Down

0 comments on commit ddf8821

Please sign in to comment.