open-collaboration-protocol
Version:
Open Collaboration Protocol implementation, part of the Open Collaboration Tools project
149 lines • 6.65 kB
JavaScript
// ******************************************************************************
// Copyright 2024 TypeFox GmbH
// This program and the accompanying materials are made available under the
// terms of the MIT License, which is available in the project root.
// ******************************************************************************
import { AbstractBroadcastConnection } from './messaging/abstract-connection.js';
import { Messages } from './messages.js';
export function createConnection(options) {
return new ProtocolBroadcastConnectionImpl(options);
}
const EMTPY_HANDLER = () => { };
export class ProtocolBroadcastConnectionImpl extends AbstractBroadcastConnection {
room = {
onJoin: handler => this.onBroadcast(Messages.Room.Joined, (origin, peer) => {
this.onDidJoinRoom(peer);
handler(origin, peer);
}),
leave: () => this.sendNotification(Messages.Room.Leave, ''),
onLeave: handler => this.onBroadcast(Messages.Room.Left, (origin, peer) => {
this.onDidLeaveRoom(peer);
handler(origin, peer);
}),
onClose: handler => this.onBroadcast(Messages.Room.Closed, (origin) => {
this.onDidClose();
handler(origin);
}),
onPermissions: handler => this.onBroadcast(Messages.Room.PermissionsUpdated, handler),
updatePermissions: permissions => this.sendBroadcast(Messages.Room.PermissionsUpdated, permissions)
};
peer = {
onJoinRequest: handler => this.onRequest(Messages.Peer.Join, handler),
onInfo: handler => this.onNotification(Messages.Peer.Info, handler),
onInit: handler => this.onNotification(Messages.Peer.Init, async (origin, response) => {
this.onDidInit(response);
handler(origin, response);
}),
init: (target, request) => this.sendNotification(Messages.Peer.Init, target, request)
};
fs = {
onReadFile: handler => this.onRequest(Messages.FileSystem.ReadFile, handler),
readFile: (target, path) => this.sendRequest(Messages.FileSystem.ReadFile, target, path),
onWriteFile: handler => this.onRequest(Messages.FileSystem.WriteFile, handler),
writeFile: (target, path, content) => this.sendRequest(Messages.FileSystem.WriteFile, target, path, content),
onReaddir: handler => this.onRequest(Messages.FileSystem.ReadDir, handler),
readdir: (target, path) => this.sendRequest(Messages.FileSystem.ReadDir, target, path),
onStat: handler => this.onRequest(Messages.FileSystem.Stat, handler),
stat: (target, path) => this.sendRequest(Messages.FileSystem.Stat, target, path),
onMkdir: handler => this.onRequest(Messages.FileSystem.Mkdir, handler),
mkdir: (target, path) => this.sendRequest(Messages.FileSystem.Mkdir, target, path),
onDelete: handler => this.onRequest(Messages.FileSystem.Delete, handler),
delete: (target, path) => this.sendRequest(Messages.FileSystem.Delete, target, path),
onRename: handler => this.onRequest(Messages.FileSystem.Rename, handler),
rename: (target, from, to) => this.sendRequest(Messages.FileSystem.Rename, target, from, to),
onChange: handler => this.onBroadcast(Messages.FileSystem.Change, handler),
change: event => this.sendBroadcast(Messages.FileSystem.Change, event)
};
editor = {
onOpen: handler => this.onNotification(Messages.Editor.Open, handler),
open: (target, path) => this.sendNotification(Messages.Editor.Open, target, path),
onClose: handler => this.onBroadcast(Messages.Editor.Close, handler),
close: path => this.sendBroadcast(Messages.Editor.Close, path)
};
sync = {
onDataUpdate: handler => {
this.onBroadcast(Messages.Sync.DataUpdate, handler);
this.onNotification(Messages.Sync.DataNotify, handler);
},
dataUpdate: async (target, data) => {
if (typeof target === 'object') {
await this.sendBroadcast(Messages.Sync.DataUpdate, target);
}
else {
await this.sendNotification(Messages.Sync.DataNotify, target, data);
}
},
onAwarenessUpdate: handler => {
this.onBroadcast(Messages.Sync.AwarenessUpdate, handler);
this.onNotification(Messages.Sync.AwarenessNotify, handler);
},
awarenessUpdate: async (target, data) => {
if (typeof target === 'object') {
await this.sendBroadcast(Messages.Sync.AwarenessUpdate, target);
}
else {
await this.sendNotification(Messages.Sync.AwarenessNotify, target, data);
}
},
onAwarenessQuery: handler => this.onBroadcast(Messages.Sync.AwarenessQuery, handler),
awarenessQuery: () => this.sendBroadcast(Messages.Sync.AwarenessQuery)
};
// Track peers manually for their public encryption keys
peers = new Map();
constructor(options) {
super({
privateKey: options.privateKey,
transport: options.transport
});
if (options.host) {
this.onDidJoinRoom(options.host);
}
else {
this.ready();
}
// Ensure that the peer handlers are called
this.room.onJoin(EMTPY_HANDLER);
this.room.onLeave(EMTPY_HANDLER);
this.room.onClose(EMTPY_HANDLER);
this.peer.onInit(EMTPY_HANDLER);
}
getPublicKey(origin) {
const peer = this.peers.get(origin);
if (peer) {
return {
peerId: peer.id,
publicKey: peer.metadata.encryption.publicKey,
supportedCompression: peer.metadata.compression.supported
};
}
else {
throw new Error('No public key found for origin ' + origin);
}
}
onDidJoinRoom(peer) {
this.peers.set(peer.id, peer);
}
onDidLeaveRoom(peer) {
this.peers.delete(peer.id);
}
onDidClose() {
this.peers.clear();
}
onDidInit(response) {
for (const peer of [response.host, ...response.guests]) {
this.peers.set(peer.id, peer);
}
this.ready();
}
getPublicKeys() {
return Array.from(this.peers.values()).map(peer => ({
peerId: peer.id,
publicKey: peer.metadata.encryption.publicKey,
supportedCompression: peer.metadata.compression.supported
}));
}
getPublicKeysLength() {
return this.peers.size;
}
}
//# sourceMappingURL=connection.js.map