@loopback/socketio
Version:
LoopBack's WebSocket server based on socket.io
138 lines • 5.8 kB
JavaScript
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
// Node module: @loopback/socketio
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
Object.defineProperty(exports, "__esModule", { value: true });
exports.SocketIoControllerFactory = exports.SocketIoConnectionContext = void 0;
const core_1 = require("@loopback/core");
const decorators_1 = require("./decorators");
const keys_1 = require("./keys");
/**
* Request context for a socket.io request
*/
class SocketIoConnectionContext extends core_1.Context {
constructor(socket, parent) {
super(parent);
this.socket = socket;
}
}
exports.SocketIoConnectionContext = SocketIoConnectionContext;
/**
* A factory to instantiate socket.io controllers
*/
class SocketIoControllerFactory {
constructor(parentCtx, controllerClass, socket) {
this.controllerClass = controllerClass;
this.connCtx = new SocketIoConnectionContext(socket, parentCtx);
this.connCtx.bind(keys_1.SocketIoBindings.SOCKET).to(this.connCtx.socket);
this.connCtx.bind(core_1.CoreBindings.CONTROLLER_CLASS).to(this.controllerClass);
this.connCtx
.bind(core_1.CoreBindings.CONTROLLER_CURRENT)
.toClass(controllerClass)
.inScope(core_1.BindingScope.SINGLETON);
}
async create() {
// Instantiate the controller instance
this.controller = await this.connCtx.get(core_1.CoreBindings.CONTROLLER_CURRENT);
await this.setup();
return this.controller;
}
/**
* Set up the controller for the given socket
*/
async setup() {
await this.connect();
this.registerSubscribeMethods();
}
async connect() {
const connectMethods = this.getDecoratedMethodsForConnect();
for (const methodName in connectMethods) {
await (0, core_1.invokeMethod)(this.controller, methodName, this.connCtx, [
this.connCtx.socket,
]);
}
}
registerSubscribeMethods() {
const methodsByEventHandler = this.getDecorateSubscribeMethodsByEventName();
const regexMethodsHandlers = new Map();
const methodHandlers = new Map();
methodsByEventHandler.forEach(eventMatcherInfo => {
const { matcher, methodNames } = eventMatcherInfo;
methodNames.forEach(methodName => {
var _a;
let handler = methodHandlers.get(methodName);
if (!handler) {
handler = this.getCallback(methodName);
methodHandlers.set(methodName, handler);
}
if (matcher instanceof RegExp) {
const handlers = (_a = regexMethodsHandlers.get(matcher)) !== null && _a !== void 0 ? _a : [];
handlers.push(handler);
regexMethodsHandlers.set(matcher, handlers);
}
else {
this.connCtx.socket.on(matcher, handler);
}
});
});
// Register event handlers with regexp
if (regexMethodsHandlers.size) {
// Use a socket middleware to match event names with regexp
// eslint-disable-next-line @typescript-eslint/no-misused-promises
this.connCtx.socket.use(async (packet, next) => {
const [eventName, ...args] = packet;
for (const iterator of regexMethodsHandlers.entries()) {
const [regex, handlers] = iterator;
if (eventName.match(regex)) {
for (const handler of handlers) {
await handler(args);
}
}
}
next();
});
}
}
getDecoratedMethodsForConnect() {
return this.getAllMethodMetadataForKey(decorators_1.SOCKET_IO_CONNECT_METADATA);
}
getDecorateSubscribeMethodsByEventName() {
var _a;
const eventMatchersInfo = new Map();
const subscribeMethods = this.getDecorateSubscribeMethods();
for (const methodName in subscribeMethods) {
for (const matcher of subscribeMethods[methodName]) {
const matcherString = matcher.toString();
const eventMatcherInfo = (_a = eventMatchersInfo.get(matcherString)) !== null && _a !== void 0 ? _a : {
matcher: matcher,
methodNames: [],
};
eventMatcherInfo.methodNames.push(methodName);
eventMatchersInfo.set(matcherString, eventMatcherInfo);
}
}
return eventMatchersInfo;
}
getDecorateSubscribeMethods() {
return this.getAllMethodMetadataForKey(decorators_1.SOCKET_IO_SUBSCRIBE_METADATA);
}
getAllMethodMetadataForKey(metadataAccessor) {
var _a;
return ((_a = core_1.MetadataInspector.getAllMethodMetadata(metadataAccessor, this.controllerClass.prototype)) !== null && _a !== void 0 ? _a : {});
}
getCallback(methodName) {
return async (...args) => {
let done = async (_response) => { };
if (typeof args[args.length - 1] === 'function') {
done = args.pop();
}
const eventCtx = new core_1.Context(this.connCtx);
eventCtx.bind(keys_1.SocketIoBindings.MESSAGE).to(args);
const sequence = await eventCtx.get(keys_1.SocketIoBindings.SEQUENCE);
await sequence.handle(methodName, args, done);
};
}
}
exports.SocketIoControllerFactory = SocketIoControllerFactory;
//# sourceMappingURL=socketio-controller-factory.js.map
;