worker-websocket-ctrl
Version:
Share a websocket using a Worker/SharedWorker communicating through comlink
131 lines (130 loc) • 6.13 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkerWsSubscriptions = void 0;
const Comlink = __importStar(require("comlink"));
class WorkerWsSubscriptions {
constructor(options) {
this.pathByResource = new Map();
this.resourceByPath = new Map();
this.workers = new Map();
this.subscriptions = new Map();
this.api = options.api;
this.createWorker = options.createWorker;
if (options.getProtocols) {
const getProtocols = options.getProtocols;
this.getProtocolsProxy = Comlink.proxy((path) => __awaiter(this, void 0, void 0, function* () {
const resource = this.resourceByPath.get(path);
if (!resource) {
console.error(`Unable to get resource name for path ${path}`);
return null;
}
return getProtocols(resource);
}));
}
if (options.autoUnsubscribe) {
this.autoUnsubscribeBinded = options.autoUnsubscribe.bind(this);
}
}
subscribe(ctx, resource, onEvent, hooks = {}) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let subscriptions = this.subscriptions.get(resource);
if (subscriptions == null) {
subscriptions = new Map();
this.subscriptions.set(resource, subscriptions);
}
else if (subscriptions.has(this)) {
return;
}
(_a = this.autoUnsubscribeBinded) === null || _a === void 0 ? void 0 : _a.call(this, ctx, () => this.unsubscribe(ctx, resource));
let workerWs;
const currentWorker = this.workers.get(resource);
let _worker;
if (!currentWorker) {
_worker = this.createWorker(resource);
if (!_worker) {
this.checkSubscriptions(resource);
throw new Error(`Unable to create worker for resource ${resource}`);
}
workerWs = Comlink.wrap(_worker instanceof Worker ? _worker : _worker.port);
const path = yield workerWs.path;
this.workers.set(resource, workerWs);
this.pathByResource.set(resource, path);
this.resourceByPath.set(path, resource);
}
else {
workerWs = currentWorker;
}
yield workerWs.start(typeof this.api === 'string' ? this.api : this.api(), this.getProtocolsProxy);
const unsubscribe = yield workerWs.subscribe(Comlink.proxy((data) => {
onEvent(data);
}), Comlink.proxy(pong => {
pong();
}), Comlink.proxy(() => {
var _a;
(_a = hooks.onConnect) === null || _a === void 0 ? void 0 : _a.call(hooks);
}), Comlink.proxy(() => {
var _a;
(_a = hooks.onDisconnect) === null || _a === void 0 ? void 0 : _a.call(hooks);
}), Comlink.proxy(() => {
var _a, _b;
(_a = hooks.onConnect) === null || _a === void 0 ? void 0 : _a.call(hooks);
console.error('Connection closed from the worker');
(_b = this.subscriptions.get(resource)) === null || _b === void 0 ? void 0 : _b.delete(ctx);
this.checkSubscriptions(resource);
}));
subscriptions.set(ctx, { unsubscribe });
return (msg) => workerWs.sendMessage(msg);
});
}
unsubscribe(ctx, ...resources) {
for (const resource of resources) {
const resourceSubs = this.subscriptions.get(resource);
const subscription = resourceSubs === null || resourceSubs === void 0 ? void 0 : resourceSubs.get(ctx);
if (subscription) {
subscription.unsubscribe();
resourceSubs === null || resourceSubs === void 0 ? void 0 : resourceSubs.delete(ctx);
this.checkSubscriptions(resource);
}
}
}
checkSubscriptions(resource) {
var _a;
if (((_a = this.subscriptions.get(resource)) === null || _a === void 0 ? void 0 : _a.size) === 0) {
this.workers.delete(resource);
const path = this.pathByResource.get(resource);
this.pathByResource.delete(resource);
if (path)
this.resourceByPath.delete(path);
}
}
}
exports.WorkerWsSubscriptions = WorkerWsSubscriptions;