@pkerschbaum/code-oss-file-service
Version:
VS Code ([microsoft/vscode](https://github.com/microsoft/vscode)) includes a rich "`FileService`" and "`DiskFileSystemProvider`" abstraction built on top of Node.js core modules (`fs`, `path`) and Electron's `shell` module. This package allows to use that
968 lines • 41.1 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
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.IPCLogger = exports.logWithColors = exports.ProxyChannel = exports.StaticRouter = exports.getNextTickChannel = exports.getDelayedChannel = exports.IPCClient = exports.IPCServer = exports.ChannelClient = exports.RequestInitiator = exports.ChannelServer = exports.ResponseType = exports.RequestType = void 0;
const arrays_1 = require("../../../../base/common/arrays");
const async_1 = require("../../../../base/common/async");
const buffer_1 = require("../../../../base/common/buffer");
const cancellation_1 = require("../../../../base/common/cancellation");
const decorators_1 = require("../../../../base/common/decorators");
const errors = require("../../../../base/common/errors");
const event_1 = require("../../../../base/common/event");
const lifecycle_1 = require("../../../../base/common/lifecycle");
const marshalling_1 = require("../../../../base/common/marshalling");
const strings = require("../../../../base/common/strings");
const types_1 = require("../../../../base/common/types");
var RequestType;
(function (RequestType) {
RequestType[RequestType["Promise"] = 100] = "Promise";
RequestType[RequestType["PromiseCancel"] = 101] = "PromiseCancel";
RequestType[RequestType["EventListen"] = 102] = "EventListen";
RequestType[RequestType["EventDispose"] = 103] = "EventDispose";
})(RequestType = exports.RequestType || (exports.RequestType = {}));
function requestTypeToStr(type) {
switch (type) {
case 100 /* Promise */:
return 'req';
case 101 /* PromiseCancel */:
return 'cancel';
case 102 /* EventListen */:
return 'subscribe';
case 103 /* EventDispose */:
return 'unsubscribe';
}
}
var ResponseType;
(function (ResponseType) {
ResponseType[ResponseType["Initialize"] = 200] = "Initialize";
ResponseType[ResponseType["PromiseSuccess"] = 201] = "PromiseSuccess";
ResponseType[ResponseType["PromiseError"] = 202] = "PromiseError";
ResponseType[ResponseType["PromiseErrorObj"] = 203] = "PromiseErrorObj";
ResponseType[ResponseType["EventFire"] = 204] = "EventFire";
})(ResponseType = exports.ResponseType || (exports.ResponseType = {}));
function responseTypeToStr(type) {
switch (type) {
case 200 /* Initialize */:
return `init`;
case 201 /* PromiseSuccess */:
return `reply:`;
case 202 /* PromiseError */:
case 203 /* PromiseErrorObj */:
return `replyErr:`;
case 204 /* EventFire */:
return `event:`;
}
}
var State;
(function (State) {
State[State["Uninitialized"] = 0] = "Uninitialized";
State[State["Idle"] = 1] = "Idle";
})(State || (State = {}));
class BufferReader {
constructor(buffer) {
this.buffer = buffer;
this.pos = 0;
}
read(bytes) {
const result = this.buffer.slice(this.pos, this.pos + bytes);
this.pos += result.byteLength;
return result;
}
}
class BufferWriter {
constructor() {
this.buffers = [];
}
get buffer() {
return buffer_1.VSBuffer.concat(this.buffers);
}
write(buffer) {
this.buffers.push(buffer);
}
}
var DataType;
(function (DataType) {
DataType[DataType["Undefined"] = 0] = "Undefined";
DataType[DataType["String"] = 1] = "String";
DataType[DataType["Buffer"] = 2] = "Buffer";
DataType[DataType["VSBuffer"] = 3] = "VSBuffer";
DataType[DataType["Array"] = 4] = "Array";
DataType[DataType["Object"] = 5] = "Object";
})(DataType || (DataType = {}));
function createSizeBuffer(size) {
const result = buffer_1.VSBuffer.alloc(4);
result.writeUInt32BE(size, 0);
return result;
}
function readSizeBuffer(reader) {
return reader.read(4).readUInt32BE(0);
}
function createOneByteBuffer(value) {
const result = buffer_1.VSBuffer.alloc(1);
result.writeUInt8(value, 0);
return result;
}
const BufferPresets = {
Undefined: createOneByteBuffer(DataType.Undefined),
String: createOneByteBuffer(DataType.String),
Buffer: createOneByteBuffer(DataType.Buffer),
VSBuffer: createOneByteBuffer(DataType.VSBuffer),
Array: createOneByteBuffer(DataType.Array),
Object: createOneByteBuffer(DataType.Object),
};
const hasBuffer = (typeof Buffer !== 'undefined');
function serialize(writer, data) {
if (typeof data === 'undefined') {
writer.write(BufferPresets.Undefined);
}
else if (typeof data === 'string') {
const buffer = buffer_1.VSBuffer.fromString(data);
writer.write(BufferPresets.String);
writer.write(createSizeBuffer(buffer.byteLength));
writer.write(buffer);
}
else if (hasBuffer && Buffer.isBuffer(data)) {
const buffer = buffer_1.VSBuffer.wrap(data);
writer.write(BufferPresets.Buffer);
writer.write(createSizeBuffer(buffer.byteLength));
writer.write(buffer);
}
else if (data instanceof buffer_1.VSBuffer) {
writer.write(BufferPresets.VSBuffer);
writer.write(createSizeBuffer(data.byteLength));
writer.write(data);
}
else if (Array.isArray(data)) {
writer.write(BufferPresets.Array);
writer.write(createSizeBuffer(data.length));
for (const el of data) {
serialize(writer, el);
}
}
else {
const buffer = buffer_1.VSBuffer.fromString(JSON.stringify(data));
writer.write(BufferPresets.Object);
writer.write(createSizeBuffer(buffer.byteLength));
writer.write(buffer);
}
}
function deserialize(reader) {
const type = reader.read(1).readUInt8(0);
switch (type) {
case DataType.Undefined: return undefined;
case DataType.String: return reader.read(readSizeBuffer(reader)).toString();
case DataType.Buffer: return reader.read(readSizeBuffer(reader)).buffer;
case DataType.VSBuffer: return reader.read(readSizeBuffer(reader));
case DataType.Array: {
const length = readSizeBuffer(reader);
const result = [];
for (let i = 0; i < length; i++) {
result.push(deserialize(reader));
}
return result;
}
case DataType.Object: return JSON.parse(reader.read(readSizeBuffer(reader)).toString());
}
}
class ChannelServer {
constructor(protocol, ctx, logger = null, timeoutDelay = 1000) {
this.protocol = protocol;
this.ctx = ctx;
this.logger = logger;
this.timeoutDelay = timeoutDelay;
this.channels = new Map();
this.activeRequests = new Map();
// Requests might come in for channels which are not yet registered.
// They will timeout after `timeoutDelay`.
this.pendingRequests = new Map();
this.protocolListener = this.protocol.onMessage(msg => this.onRawMessage(msg));
this.sendResponse({ type: 200 /* Initialize */ });
}
registerChannel(channelName, channel) {
this.channels.set(channelName, channel);
// https://github.com/microsoft/vscode/issues/72531
setTimeout(() => this.flushPendingRequests(channelName), 0);
}
sendResponse(response) {
switch (response.type) {
case 200 /* Initialize */: {
const msgLength = this.send([response.type]);
if (this.logger) {
this.logger.logOutgoing(msgLength, 0, 1 /* OtherSide */, responseTypeToStr(response.type));
}
return;
}
case 201 /* PromiseSuccess */:
case 202 /* PromiseError */:
case 204 /* EventFire */:
case 203 /* PromiseErrorObj */: {
const msgLength = this.send([response.type, response.id], response.data);
if (this.logger) {
this.logger.logOutgoing(msgLength, response.id, 1 /* OtherSide */, responseTypeToStr(response.type), response.data);
}
return;
}
}
}
send(header, body = undefined) {
const writer = new BufferWriter();
serialize(writer, header);
serialize(writer, body);
return this.sendBuffer(writer.buffer);
}
sendBuffer(message) {
try {
this.protocol.send(message);
return message.byteLength;
}
catch (err) {
// noop
return 0;
}
}
onRawMessage(message) {
const reader = new BufferReader(message);
const header = deserialize(reader);
const body = deserialize(reader);
const type = header[0];
switch (type) {
case 100 /* Promise */:
if (this.logger) {
this.logger.logIncoming(message.byteLength, header[1], 1 /* OtherSide */, `${requestTypeToStr(type)}: ${header[2]}.${header[3]}`, body);
}
return this.onPromise({ type, id: header[1], channelName: header[2], name: header[3], arg: body });
case 102 /* EventListen */:
if (this.logger) {
this.logger.logIncoming(message.byteLength, header[1], 1 /* OtherSide */, `${requestTypeToStr(type)}: ${header[2]}.${header[3]}`, body);
}
return this.onEventListen({ type, id: header[1], channelName: header[2], name: header[3], arg: body });
case 101 /* PromiseCancel */:
if (this.logger) {
this.logger.logIncoming(message.byteLength, header[1], 1 /* OtherSide */, `${requestTypeToStr(type)}`);
}
return this.disposeActiveRequest({ type, id: header[1] });
case 103 /* EventDispose */:
if (this.logger) {
this.logger.logIncoming(message.byteLength, header[1], 1 /* OtherSide */, `${requestTypeToStr(type)}`);
}
return this.disposeActiveRequest({ type, id: header[1] });
}
}
onPromise(request) {
const channel = this.channels.get(request.channelName);
if (!channel) {
this.collectPendingRequest(request);
return;
}
const cancellationTokenSource = new cancellation_1.CancellationTokenSource();
let promise;
try {
promise = channel.call(this.ctx, request.name, request.arg, cancellationTokenSource.token);
}
catch (err) {
promise = Promise.reject(err);
}
const id = request.id;
promise.then(data => {
this.sendResponse({ id, data, type: 201 /* PromiseSuccess */ });
this.activeRequests.delete(request.id);
}, err => {
if (err instanceof Error) {
this.sendResponse({
id, data: {
message: err.message,
name: err.name,
stack: err.stack ? (err.stack.split ? err.stack.split('\n') : err.stack) : undefined
}, type: 202 /* PromiseError */
});
}
else {
this.sendResponse({ id, data: err, type: 203 /* PromiseErrorObj */ });
}
this.activeRequests.delete(request.id);
});
const disposable = (0, lifecycle_1.toDisposable)(() => cancellationTokenSource.cancel());
this.activeRequests.set(request.id, disposable);
}
onEventListen(request) {
const channel = this.channels.get(request.channelName);
if (!channel) {
this.collectPendingRequest(request);
return;
}
const id = request.id;
const event = channel.listen(this.ctx, request.name, request.arg);
const disposable = event(data => this.sendResponse({ id, data, type: 204 /* EventFire */ }));
this.activeRequests.set(request.id, disposable);
}
disposeActiveRequest(request) {
const disposable = this.activeRequests.get(request.id);
if (disposable) {
disposable.dispose();
this.activeRequests.delete(request.id);
}
}
collectPendingRequest(request) {
let pendingRequests = this.pendingRequests.get(request.channelName);
if (!pendingRequests) {
pendingRequests = [];
this.pendingRequests.set(request.channelName, pendingRequests);
}
const timer = setTimeout(() => {
console.error(`Unknown channel: ${request.channelName}`);
if (request.type === 100 /* Promise */) {
this.sendResponse({
id: request.id,
data: { name: 'Unknown channel', message: `Channel name '${request.channelName}' timed out after ${this.timeoutDelay}ms`, stack: undefined },
type: 202 /* PromiseError */
});
}
}, this.timeoutDelay);
pendingRequests.push({ request, timeoutTimer: timer });
}
flushPendingRequests(channelName) {
const requests = this.pendingRequests.get(channelName);
if (requests) {
for (const request of requests) {
clearTimeout(request.timeoutTimer);
switch (request.request.type) {
case 100 /* Promise */:
this.onPromise(request.request);
break;
case 102 /* EventListen */:
this.onEventListen(request.request);
break;
}
}
this.pendingRequests.delete(channelName);
}
}
dispose() {
if (this.protocolListener) {
this.protocolListener.dispose();
this.protocolListener = null;
}
this.activeRequests.forEach(d => d.dispose());
this.activeRequests.clear();
}
}
exports.ChannelServer = ChannelServer;
var RequestInitiator;
(function (RequestInitiator) {
RequestInitiator[RequestInitiator["LocalSide"] = 0] = "LocalSide";
RequestInitiator[RequestInitiator["OtherSide"] = 1] = "OtherSide";
})(RequestInitiator = exports.RequestInitiator || (exports.RequestInitiator = {}));
class ChannelClient {
constructor(protocol, logger = null) {
this.protocol = protocol;
this.isDisposed = false;
this.state = State.Uninitialized;
this.activeRequests = new Set();
this.handlers = new Map();
this.lastRequestId = 0;
this._onDidInitialize = new event_1.Emitter();
this.onDidInitialize = this._onDidInitialize.event;
this.protocolListener = this.protocol.onMessage(msg => this.onBuffer(msg));
this.logger = logger;
}
getChannel(channelName) {
const that = this;
return {
call(command, arg, cancellationToken) {
if (that.isDisposed) {
return Promise.reject(errors.canceled());
}
return that.requestPromise(channelName, command, arg, cancellationToken);
},
listen(event, arg) {
if (that.isDisposed) {
return Promise.reject(errors.canceled());
}
return that.requestEvent(channelName, event, arg);
}
};
}
requestPromise(channelName, name, arg, cancellationToken = cancellation_1.CancellationToken.None) {
const id = this.lastRequestId++;
const type = 100 /* Promise */;
const request = { id, type, channelName, name, arg };
if (cancellationToken.isCancellationRequested) {
return Promise.reject(errors.canceled());
}
let disposable;
const result = new Promise((c, e) => {
if (cancellationToken.isCancellationRequested) {
return e(errors.canceled());
}
const doRequest = () => {
const handler = response => {
switch (response.type) {
case 201 /* PromiseSuccess */:
this.handlers.delete(id);
c(response.data);
break;
case 202 /* PromiseError */: {
this.handlers.delete(id);
const error = new Error(response.data.message);
error.stack = response.data.stack;
error.name = response.data.name;
e(error);
break;
}
case 203 /* PromiseErrorObj */:
this.handlers.delete(id);
e(response.data);
break;
}
};
this.handlers.set(id, handler);
this.sendRequest(request);
};
let uninitializedPromise = null;
if (this.state === State.Idle) {
doRequest();
}
else {
uninitializedPromise = (0, async_1.createCancelablePromise)(_ => this.whenInitialized());
uninitializedPromise.then(() => {
uninitializedPromise = null;
doRequest();
});
}
const cancel = () => {
if (uninitializedPromise) {
uninitializedPromise.cancel();
uninitializedPromise = null;
}
else {
this.sendRequest({ id, type: 101 /* PromiseCancel */ });
}
e(errors.canceled());
};
const cancellationTokenListener = cancellationToken.onCancellationRequested(cancel);
disposable = (0, lifecycle_1.combinedDisposable)((0, lifecycle_1.toDisposable)(cancel), cancellationTokenListener);
this.activeRequests.add(disposable);
});
return result.finally(() => { this.activeRequests.delete(disposable); });
}
requestEvent(channelName, name, arg) {
const id = this.lastRequestId++;
const type = 102 /* EventListen */;
const request = { id, type, channelName, name, arg };
let uninitializedPromise = null;
const emitter = new event_1.Emitter({
onFirstListenerAdd: () => {
uninitializedPromise = (0, async_1.createCancelablePromise)(_ => this.whenInitialized());
uninitializedPromise.then(() => {
uninitializedPromise = null;
this.activeRequests.add(emitter);
this.sendRequest(request);
});
},
onLastListenerRemove: () => {
if (uninitializedPromise) {
uninitializedPromise.cancel();
uninitializedPromise = null;
}
else {
this.activeRequests.delete(emitter);
this.sendRequest({ id, type: 103 /* EventDispose */ });
}
}
});
const handler = (res) => emitter.fire(res.data);
this.handlers.set(id, handler);
return emitter.event;
}
sendRequest(request) {
switch (request.type) {
case 100 /* Promise */:
case 102 /* EventListen */: {
const msgLength = this.send([request.type, request.id, request.channelName, request.name], request.arg);
if (this.logger) {
this.logger.logOutgoing(msgLength, request.id, 0 /* LocalSide */, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg);
}
return;
}
case 101 /* PromiseCancel */:
case 103 /* EventDispose */: {
const msgLength = this.send([request.type, request.id]);
if (this.logger) {
this.logger.logOutgoing(msgLength, request.id, 0 /* LocalSide */, requestTypeToStr(request.type));
}
return;
}
}
}
send(header, body = undefined) {
const writer = new BufferWriter();
serialize(writer, header);
serialize(writer, body);
return this.sendBuffer(writer.buffer);
}
sendBuffer(message) {
try {
this.protocol.send(message);
return message.byteLength;
}
catch (err) {
// noop
return 0;
}
}
onBuffer(message) {
const reader = new BufferReader(message);
const header = deserialize(reader);
const body = deserialize(reader);
const type = header[0];
switch (type) {
case 200 /* Initialize */:
if (this.logger) {
this.logger.logIncoming(message.byteLength, 0, 0 /* LocalSide */, responseTypeToStr(type));
}
return this.onResponse({ type: header[0] });
case 201 /* PromiseSuccess */:
case 202 /* PromiseError */:
case 204 /* EventFire */:
case 203 /* PromiseErrorObj */:
if (this.logger) {
this.logger.logIncoming(message.byteLength, header[1], 0 /* LocalSide */, responseTypeToStr(type), body);
}
return this.onResponse({ type: header[0], id: header[1], data: body });
}
}
onResponse(response) {
if (response.type === 200 /* Initialize */) {
this.state = State.Idle;
this._onDidInitialize.fire();
return;
}
const handler = this.handlers.get(response.id);
if (handler) {
handler(response);
}
}
get onDidInitializePromise() {
return event_1.Event.toPromise(this.onDidInitialize);
}
whenInitialized() {
if (this.state === State.Idle) {
return Promise.resolve();
}
else {
return this.onDidInitializePromise;
}
}
dispose() {
this.isDisposed = true;
if (this.protocolListener) {
this.protocolListener.dispose();
this.protocolListener = null;
}
this.activeRequests.forEach(p => p.dispose());
this.activeRequests.clear();
}
}
__decorate([
decorators_1.memoize
], ChannelClient.prototype, "onDidInitializePromise", null);
exports.ChannelClient = ChannelClient;
/**
* An `IPCServer` is both a channel server and a routing channel
* client.
*
* As the owner of a protocol, you should extend both this
* and the `IPCClient` classes to get IPC implementations
* for your protocol.
*/
class IPCServer {
constructor(onDidClientConnect) {
this.channels = new Map();
this._connections = new Set();
this._onDidAddConnection = new event_1.Emitter();
this.onDidAddConnection = this._onDidAddConnection.event;
this._onDidRemoveConnection = new event_1.Emitter();
this.onDidRemoveConnection = this._onDidRemoveConnection.event;
onDidClientConnect(({ protocol, onDidClientDisconnect }) => {
const onFirstMessage = event_1.Event.once(protocol.onMessage);
onFirstMessage(msg => {
const reader = new BufferReader(msg);
const ctx = deserialize(reader);
const channelServer = new ChannelServer(protocol, ctx);
const channelClient = new ChannelClient(protocol);
this.channels.forEach((channel, name) => channelServer.registerChannel(name, channel));
const connection = { channelServer, channelClient, ctx };
this._connections.add(connection);
this._onDidAddConnection.fire(connection);
onDidClientDisconnect(() => {
channelServer.dispose();
channelClient.dispose();
this._connections.delete(connection);
this._onDidRemoveConnection.fire(connection);
});
});
});
}
get connections() {
const result = [];
this._connections.forEach(ctx => result.push(ctx));
return result;
}
getChannel(channelName, routerOrClientFilter) {
const that = this;
return {
call(command, arg, cancellationToken) {
let connectionPromise;
if ((0, types_1.isFunction)(routerOrClientFilter)) {
// when no router is provided, we go random client picking
let connection = (0, arrays_1.getRandomElement)(that.connections.filter(routerOrClientFilter));
connectionPromise = connection
// if we found a client, let's call on it
? Promise.resolve(connection)
// else, let's wait for a client to come along
: event_1.Event.toPromise(event_1.Event.filter(that.onDidAddConnection, routerOrClientFilter));
}
else {
connectionPromise = routerOrClientFilter.routeCall(that, command, arg);
}
const channelPromise = connectionPromise
.then(connection => connection.channelClient.getChannel(channelName));
return getDelayedChannel(channelPromise)
.call(command, arg, cancellationToken);
},
listen(event, arg) {
if ((0, types_1.isFunction)(routerOrClientFilter)) {
return that.getMulticastEvent(channelName, routerOrClientFilter, event, arg);
}
const channelPromise = routerOrClientFilter.routeEvent(that, event, arg)
.then(connection => connection.channelClient.getChannel(channelName));
return getDelayedChannel(channelPromise)
.listen(event, arg);
}
};
}
getMulticastEvent(channelName, clientFilter, eventName, arg) {
const that = this;
let disposables = new lifecycle_1.DisposableStore();
// Create an emitter which hooks up to all clients
// as soon as first listener is added. It also
// disconnects from all clients as soon as the last listener
// is removed.
const emitter = new event_1.Emitter({
onFirstListenerAdd: () => {
disposables = new lifecycle_1.DisposableStore();
// The event multiplexer is useful since the active
// client list is dynamic. We need to hook up and disconnection
// to/from clients as they come and go.
const eventMultiplexer = new event_1.EventMultiplexer();
const map = new Map();
const onDidAddConnection = (connection) => {
const channel = connection.channelClient.getChannel(channelName);
const event = channel.listen(eventName, arg);
const disposable = eventMultiplexer.add(event);
map.set(connection, disposable);
};
const onDidRemoveConnection = (connection) => {
const disposable = map.get(connection);
if (!disposable) {
return;
}
disposable.dispose();
map.delete(connection);
};
that.connections.filter(clientFilter).forEach(onDidAddConnection);
event_1.Event.filter(that.onDidAddConnection, clientFilter)(onDidAddConnection, undefined, disposables);
that.onDidRemoveConnection(onDidRemoveConnection, undefined, disposables);
eventMultiplexer.event(emitter.fire, emitter, disposables);
disposables.add(eventMultiplexer);
},
onLastListenerRemove: () => {
disposables.dispose();
}
});
return emitter.event;
}
registerChannel(channelName, channel) {
this.channels.set(channelName, channel);
this._connections.forEach(connection => {
connection.channelServer.registerChannel(channelName, channel);
});
}
dispose() {
this.channels.clear();
this._connections.clear();
this._onDidAddConnection.dispose();
this._onDidRemoveConnection.dispose();
}
}
exports.IPCServer = IPCServer;
/**
* An `IPCClient` is both a channel client and a channel server.
*
* As the owner of a protocol, you should extend both this
* and the `IPCClient` classes to get IPC implementations
* for your protocol.
*/
class IPCClient {
constructor(protocol, ctx, ipcLogger = null) {
const writer = new BufferWriter();
serialize(writer, ctx);
protocol.send(writer.buffer);
this.channelClient = new ChannelClient(protocol, ipcLogger);
this.channelServer = new ChannelServer(protocol, ctx, ipcLogger);
}
getChannel(channelName) {
return this.channelClient.getChannel(channelName);
}
registerChannel(channelName, channel) {
this.channelServer.registerChannel(channelName, channel);
}
dispose() {
this.channelClient.dispose();
this.channelServer.dispose();
}
}
exports.IPCClient = IPCClient;
function getDelayedChannel(promise) {
return {
call(command, arg, cancellationToken) {
return promise.then(c => c.call(command, arg, cancellationToken));
},
listen(event, arg) {
const relay = new event_1.Relay();
promise.then(c => relay.input = c.listen(event, arg));
return relay.event;
}
};
}
exports.getDelayedChannel = getDelayedChannel;
function getNextTickChannel(channel) {
let didTick = false;
return {
call(command, arg, cancellationToken) {
if (didTick) {
return channel.call(command, arg, cancellationToken);
}
return (0, async_1.timeout)(0)
.then(() => didTick = true)
.then(() => channel.call(command, arg, cancellationToken));
},
listen(event, arg) {
if (didTick) {
return channel.listen(event, arg);
}
const relay = new event_1.Relay();
(0, async_1.timeout)(0)
.then(() => didTick = true)
.then(() => relay.input = channel.listen(event, arg));
return relay.event;
}
};
}
exports.getNextTickChannel = getNextTickChannel;
class StaticRouter {
constructor(fn) {
this.fn = fn;
}
routeCall(hub) {
return this.route(hub);
}
routeEvent(hub) {
return this.route(hub);
}
route(hub) {
return __awaiter(this, void 0, void 0, function* () {
for (const connection of hub.connections) {
if (yield Promise.resolve(this.fn(connection.ctx))) {
return Promise.resolve(connection);
}
}
yield event_1.Event.toPromise(hub.onDidAddConnection);
return yield this.route(hub);
});
}
}
exports.StaticRouter = StaticRouter;
/**
* Use ProxyChannels to automatically wrapping and unwrapping
* services to/from IPC channels, instead of manually wrapping
* each service method and event.
*
* Restrictions:
* - If marshalling is enabled, only `URI` and `RegExp` is converted
* automatically for you
* - Events must follow the naming convention `onUpperCase`
* - `CancellationToken` is currently not supported
* - If a context is provided, you can use `AddFirstParameterToFunctions`
* utility to signal this in the receiving side type
*/
var ProxyChannel;
(function (ProxyChannel) {
function fromService(service, options) {
const handler = service;
const disableMarshalling = options && options.disableMarshalling;
// Buffer any event that should be supported by
// iterating over all property keys and finding them
const mapEventNameToEvent = new Map();
for (const key in handler) {
if (propertyIsEvent(key)) {
mapEventNameToEvent.set(key, event_1.Event.buffer(handler[key], true));
}
}
return new class {
listen(_, event, arg) {
const eventImpl = mapEventNameToEvent.get(event);
if (eventImpl) {
return eventImpl;
}
if (propertyIsDynamicEvent(event)) {
const target = handler[event];
if (typeof target === 'function') {
return target.call(handler, arg);
}
}
throw new Error(`Event not found: ${event}`);
}
call(_, command, args) {
const target = handler[command];
if (typeof target === 'function') {
// Revive unless marshalling disabled
if (!disableMarshalling && Array.isArray(args)) {
for (let i = 0; i < args.length; i++) {
args[i] = (0, marshalling_1.revive)(args[i]);
}
}
return target.apply(handler, args);
}
throw new Error(`Method not found: ${command}`);
}
};
}
ProxyChannel.fromService = fromService;
function toService(channel, options) {
const disableMarshalling = options && options.disableMarshalling;
return new Proxy({}, {
get(_target, propKey) {
var _a;
if (typeof propKey === 'string') {
// Check for predefined values
if ((_a = options === null || options === void 0 ? void 0 : options.properties) === null || _a === void 0 ? void 0 : _a.has(propKey)) {
return options.properties.get(propKey);
}
// Dynamic Event
if (propertyIsDynamicEvent(propKey)) {
return function (arg) {
return channel.listen(propKey, arg);
};
}
// Event
if (propertyIsEvent(propKey)) {
return channel.listen(propKey);
}
// Function
return function (...args) {
return __awaiter(this, void 0, void 0, function* () {
// Add context if any
let methodArgs;
if (options && !(0, types_1.isUndefinedOrNull)(options.context)) {
methodArgs = [options.context, ...args];
}
else {
methodArgs = args;
}
const result = yield channel.call(propKey, methodArgs);
// Revive unless marshalling disabled
if (!disableMarshalling) {
return (0, marshalling_1.revive)(result);
}
return result;
});
};
}
throw new Error(`Property not found: ${String(propKey)}`);
}
});
}
ProxyChannel.toService = toService;
function propertyIsEvent(name) {
// Assume a property is an event if it has a form of "onSomething"
return name[0] === 'o' && name[1] === 'n' && strings.isUpperAsciiLetter(name.charCodeAt(2));
}
function propertyIsDynamicEvent(name) {
// Assume a property is a dynamic event (a method that returns an event) if it has a form of "onDynamicSomething"
return /^onDynamic/.test(name) && strings.isUpperAsciiLetter(name.charCodeAt(9));
}
})(ProxyChannel = exports.ProxyChannel || (exports.ProxyChannel = {}));
const colorTables = [
['#2977B1', '#FC802D', '#34A13A', '#D3282F', '#9366BA'],
['#8B564C', '#E177C0', '#7F7F7F', '#BBBE3D', '#2EBECD']
];
function prettyWithoutArrays(data) {
if (Array.isArray(data)) {
return data;
}
if (data && typeof data === 'object' && typeof data.toString === 'function') {
let result = data.toString();
if (result !== '[object Object]') {
return result;
}
}
return data;
}
function pretty(data) {
if (Array.isArray(data)) {
return data.map(prettyWithoutArrays);
}
return prettyWithoutArrays(data);
}
function logWithColors(direction, totalLength, msgLength, req, initiator, str, data) {
data = pretty(data);
const colorTable = colorTables[initiator];
const color = colorTable[req % colorTable.length];
let args = [`%c[${direction}]%c[${String(totalLength).padStart(7, ' ')}]%c[len: ${String(msgLength).padStart(5, ' ')}]%c${String(req).padStart(5, ' ')} - ${str}`, 'color: darkgreen', 'color: grey', 'color: grey', `color: ${color}`];
if (/\($/.test(str)) {
args = args.concat(data);
args.push(')');
}
else {
args.push(data);
}
console.log.apply(console, args);
}
exports.logWithColors = logWithColors;
class IPCLogger {
constructor(_outgoingPrefix, _incomingPrefix) {
this._outgoingPrefix = _outgoingPrefix;
this._incomingPrefix = _incomingPrefix;
this._totalIncoming = 0;
this._totalOutgoing = 0;
}
logOutgoing(msgLength, requestId, initiator, str, data) {
this._totalOutgoing += msgLength;
logWithColors(this._outgoingPrefix, this._totalOutgoing, msgLength, requestId, initiator, str, data);
}
logIncoming(msgLength, requestId, initiator, str, data) {
this._totalIncoming += msgLength;
logWithColors(this._incomingPrefix, this._totalIncoming, msgLength, requestId, initiator, str, data);
}
}
exports.IPCLogger = IPCLogger;
//# sourceMappingURL=ipc.js.map