UNPKG

@loopback/socketio

Version:

LoopBack's WebSocket server based on socket.io

138 lines 5.8 kB
"use strict"; // 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