@lvce-editor/network-process
Version:
1,561 lines (1,493 loc) • 37.9 kB
JavaScript
import { createWriteStream, createReadStream } from 'node:fs';
import { mkdir, rm } from 'node:fs/promises';
import { pipeline } from 'node:stream/promises';
import { createGunzip, createBrotliDecompress } from 'node:zlib';
import { Buffer } from 'node:buffer';
import * as NodePath from 'node:path';
import { dirname as dirname$1, resolve as resolve$1 } from 'node:path';
import { fileURLToPath } from 'node:url';
const Gnome = 'gnome';
const Windows = 'windows';
const Unknown = '';
const {
platform
} = process;
const isWindows$1 = platform === 'win32';
const getDesktop = () => {
const {
ORIGINAL_XDG_CURRENT_DESKTOP,
XDG_CURRENT_DESKTOP
} = process.env;
if (ORIGINAL_XDG_CURRENT_DESKTOP && ORIGINAL_XDG_CURRENT_DESKTOP !== 'undefined') {
if (ORIGINAL_XDG_CURRENT_DESKTOP === 'ubuntu:GNOME') {
return Gnome;
}
return ORIGINAL_XDG_CURRENT_DESKTOP;
}
if (XDG_CURRENT_DESKTOP) {
if (XDG_CURRENT_DESKTOP === 'ubuntu:GNOME') {
return Gnome;
}
return XDG_CURRENT_DESKTOP;
}
if (isWindows$1) {
return Windows;
}
return Unknown;
};
const getClipboard = async () => {
const desktop = getDesktop();
switch (desktop) {
case Gnome:
return Promise.resolve().then(function () { return ClipBoardGnome; });
case Windows:
return Promise.resolve().then(function () { return ClipBoardWindows; });
default:
return Promise.resolve().then(function () { return ClipBoardNoop; });
}
};
const readFiles$3 = async () => {
const clipboard = await getClipboard();
return clipboard.readFiles();
};
const writeFiles$3 = async (type, files) => {
const clipboard = await getClipboard();
await clipboard.writeFiles(type, files);
};
const join = (...parts) => {
return NodePath.join(...parts);
};
const dirname = path => {
return NodePath.dirname(path);
};
const normalizeLine = line => {
if (line.startsWith('Error: ')) {
return line.slice('Error: '.length);
}
if (line.startsWith('VError: ')) {
return line.slice('VError: '.length);
}
return line;
};
const getCombinedMessage = (error, message) => {
const stringifiedError = normalizeLine(`${error}`);
if (message) {
return `${message}: ${stringifiedError}`;
}
return stringifiedError;
};
const NewLine$2 = '\n';
const getNewLineIndex = (string, startIndex = undefined) => {
return string.indexOf(NewLine$2, startIndex);
};
const mergeStacks = (parent, child) => {
if (!child) {
return parent;
}
const parentNewLineIndex = getNewLineIndex(parent);
const childNewLineIndex = getNewLineIndex(child);
if (childNewLineIndex === -1) {
return parent;
}
const parentFirstLine = parent.slice(0, parentNewLineIndex);
const childRest = child.slice(childNewLineIndex);
const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
if (parentFirstLine.includes(childFirstLine)) {
return parentFirstLine + childRest;
}
return child;
};
class VError extends Error {
constructor(error, message) {
const combinedMessage = getCombinedMessage(error, message);
super(combinedMessage);
this.name = 'VError';
if (error instanceof Error) {
this.stack = mergeStacks(this.stack, error.stack);
}
if (error.codeFrame) {
// @ts-ignore
this.codeFrame = error.codeFrame;
}
if (error.code) {
// @ts-ignore
this.code = error.code;
}
}
}
const download = async (url, outFile) => {
const {
default: got,
RequestError
} = await import('got');
try {
await mkdir(dirname(outFile), {
recursive: true
});
await pipeline(got.stream(url), createWriteStream(outFile));
} catch (error) {
try {
await rm(outFile);
} catch {
// ignore
}
if (error instanceof RequestError) {
throw new VError(`Failed to download "${url}": ${error.message}`);
}
throw new VError(error, `Failed to download "${url}"`);
}
};
const downloadAndExtractTarGz = async ({
url,
outDir,
strip
}) => {
const {
got,
RequestError
} = await import('got');
const {
default: tar
} = await import('tar-fs');
try {
await pipeline(got.stream(url), createGunzip(), tar.extract(outDir, {
strip
}));
} catch (error) {
if (error && error instanceof RequestError) {
throw new VError(`Failed to download ${url}: ${error.message}`);
}
throw error;
}
};
const extractTarBr = async (inFile, outDir) => {
try {
const {
default: tar
} = await import('tar-fs');
await mkdir(outDir, {
recursive: true
});
await pipeline(createReadStream(inFile), createBrotliDecompress(), tar.extract(outDir));
} catch (error) {
throw new VError(error, `Failed to extract ${inFile}`);
}
};
const extractTarGz = async ({
inFile,
outDir,
strip
}) => {
try {
const {
default: tar
} = await import('tar-fs');
await mkdir(outDir, {
recursive: true
});
await pipeline(createReadStream(inFile), createGunzip(), tar.extract(outDir, {
strip
}));
} catch (error) {
throw new VError(error, `Failed to extract ${inFile}`);
}
};
const getUrl = async ({
method,
url
}) => {
const {
default: got
} = await import('got');
const json = await got({
url,
method: method
});
return json.url.toString();
};
class AssertionError extends Error {
constructor(message) {
super(message);
this.name = 'AssertionError';
}
}
const getType = value => {
switch (typeof value) {
case 'number':
return 'number';
case 'function':
return 'function';
case 'string':
return 'string';
case 'object':
if (value === null) {
return 'null';
}
if (Array.isArray(value)) {
return 'array';
}
return 'object';
case 'boolean':
return 'boolean';
default:
return 'unknown';
}
};
const object = value => {
const type = getType(value);
if (type !== 'object') {
throw new AssertionError('expected value to be of type object');
}
};
const number = value => {
const type = getType(value);
if (type !== 'number') {
throw new AssertionError('expected value to be of type number');
}
};
const array = value => {
const type = getType(value);
if (type !== 'array') {
throw new AssertionError('expected value to be of type array');
}
};
const string = value => {
const type = getType(value);
if (type !== 'string') {
throw new AssertionError('expected value to be of type string');
}
};
const Two = '2.0';
const callbacks = Object.create(null);
const get = id => {
return callbacks[id];
};
const remove = id => {
delete callbacks[id];
};
class JsonRpcError extends Error {
constructor(message) {
super(message);
this.name = 'JsonRpcError';
}
}
const MethodNotFound = -32601;
const Custom = -32001;
const warn = (...args) => {
console.warn(...args);
};
const resolve = (id, response) => {
const fn = get(id);
if (!fn) {
console.log(response);
warn(`callback ${id} may already be disposed`);
return;
}
fn(response);
remove(id);
};
const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
const getErrorType = prettyError => {
if (prettyError && prettyError.type) {
return prettyError.type;
}
if (prettyError && prettyError.constructor && prettyError.constructor.name) {
return prettyError.constructor.name;
}
return undefined;
};
const isAlreadyStack = line => {
return line.trim().startsWith('at ');
};
const getStack = prettyError => {
const stackString = prettyError.stack || '';
const newLineIndex = stackString.indexOf('\n');
if (newLineIndex !== -1 && !isAlreadyStack(stackString.slice(0, newLineIndex))) {
return stackString.slice(newLineIndex + 1);
}
return stackString;
};
const getErrorProperty = (error, prettyError) => {
if (error && error.code === E_COMMAND_NOT_FOUND) {
return {
code: MethodNotFound,
message: error.message,
data: error.stack
};
}
return {
code: Custom,
message: prettyError.message,
data: {
stack: getStack(prettyError),
codeFrame: prettyError.codeFrame,
type: getErrorType(prettyError),
code: prettyError.code,
name: prettyError.name
}
};
};
const create$1 = (id, error) => {
return {
jsonrpc: Two,
id,
error
};
};
const getErrorResponse = (id, error, preparePrettyError, logError) => {
const prettyError = preparePrettyError(error);
logError(error, prettyError);
const errorProperty = getErrorProperty(error, prettyError);
return create$1(id, errorProperty);
};
const create = (message, result) => {
return {
jsonrpc: Two,
id: message.id,
result: result ?? null
};
};
const getSuccessResponse = (message, result) => {
const resultProperty = result ?? null;
return create(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
return {
jsonrpc: Two,
id,
error: {
code: Custom,
// @ts-ignore
message: error.message,
data: error
}
};
};
const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
try {
const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
return getSuccessResponse(message, result);
} catch (error) {
if (ipc.canUseSimpleErrorResponse) {
return getErrorResponseSimple(message.id, error);
}
return getErrorResponse(message.id, error, preparePrettyError, logError);
}
};
const defaultPreparePrettyError = error => {
return error;
};
const defaultLogError = () => {
// ignore
};
const defaultRequiresSocket = () => {
return false;
};
const defaultResolve = resolve;
// TODO maybe remove this in v6 or v7, only accept options object to simplify the code
const normalizeParams = args => {
if (args.length === 1) {
const options = args[0];
return {
ipc: options.ipc,
message: options.message,
execute: options.execute,
resolve: options.resolve || defaultResolve,
preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
logError: options.logError || defaultLogError,
requiresSocket: options.requiresSocket || defaultRequiresSocket
};
}
return {
ipc: args[0],
message: args[1],
execute: args[2],
resolve: args[3],
preparePrettyError: args[4],
logError: args[5],
requiresSocket: args[6]
};
};
const handleJsonRpcMessage = async (...args) => {
const options = normalizeParams(args);
const {
message,
ipc,
execute,
resolve,
preparePrettyError,
logError,
requiresSocket
} = options;
if ('id' in message) {
if ('method' in message) {
const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
try {
ipc.send(response);
} catch (error) {
const errorResponse = getErrorResponse(message.id, error, preparePrettyError, logError);
ipc.send(errorResponse);
}
return;
}
resolve(message.id, message);
return;
}
if ('method' in message) {
await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
return;
}
throw new JsonRpcError('unexpected message');
};
const state = {
commands: Object.create(null)
};
const registerCommand = (key, fn) => {
state.commands[key] = fn;
};
const registerCommands = commandMap => {
for (const [key, value] of Object.entries(commandMap)) {
registerCommand(key, value);
}
};
const getCommand = key => {
return state.commands[key];
};
const execute = (command, ...args) => {
const fn = getCommand(command);
if (!fn) {
throw new Error(`Command not found ${command}`);
}
return fn(...args);
};
const prepare = error => {
return error;
};
const requiresSocket = method => {
return method === 'ElectronWebContentsView.createWebContentsView';
};
const logError = (error, prettyError) => {
console.error(error);
};
const handleMessage = event => {
return handleJsonRpcMessage(event.target, event.data, execute, resolve, prepare,
// @ts-ignore
logError, requiresSocket);
};
const handleIpc = ipc => {
ipc.addEventListener('message', handleMessage);
};
const applyIncomingIpcResponse = async (target, response, ipcId) => {
switch (response.type) {
case 'handle':
handleIpc(target);
break;
default:
throw new Error('unexpected response');
}
};
const handleIncomingIpcMessagePort = async (module, handle, message) => {
const target = await module.targetMessagePort(handle, message);
const response = module.upgradeMessagePort(handle, message);
return {
target,
response
};
};
const isMessagePort = value => {
return value && value instanceof MessagePort;
};
const isMessagePortMain = value => {
return value && value.constructor && value.constructor.name === 'MessagePortMain';
};
const isOffscreenCanvas = value => {
return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
};
const isInstanceOf = (value, constructorName) => {
return value?.constructor?.name === constructorName;
};
const isSocket = value => {
return isInstanceOf(value, 'Socket');
};
const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
const isTransferrable = value => {
for (const fn of transferrables) {
if (fn(value)) {
return true;
}
}
return false;
};
const walkValue = (value, transferrables, isTransferrable) => {
if (!value) {
return;
}
if (isTransferrable(value)) {
transferrables.push(value);
return;
}
if (Array.isArray(value)) {
for (const item of value) {
walkValue(item, transferrables, isTransferrable);
}
return;
}
if (typeof value === 'object') {
for (const property of Object.values(value)) {
walkValue(property, transferrables, isTransferrable);
}
return;
}
};
const getTransferrables = value => {
const transferrables = [];
walkValue(value, transferrables, isTransferrable);
return transferrables;
};
const removeValues = (value, toRemove) => {
if (!value) {
return value;
}
if (Array.isArray(value)) {
const newItems = [];
for (const item of value) {
if (!toRemove.includes(item)) {
newItems.push(removeValues(item, toRemove));
}
}
return newItems;
}
if (typeof value === 'object') {
const newObject = Object.create(null);
for (const [key, property] of Object.entries(value)) {
if (!toRemove.includes(property)) {
newObject[key] = removeValues(property, toRemove);
}
}
return newObject;
}
return value;
};
// workaround for electron not supporting transferrable objects
// as parameters. If the transferrable object is a parameter, in electron
// only an empty objected is received in the main process
const fixElectronParameters = value => {
const transfer = getTransferrables(value);
const newValue = removeValues(value, transfer);
return {
newValue,
transfer
};
};
const getActualDataElectron = event => {
const {
data,
ports
} = event;
if (ports.length === 0) {
return data;
}
return {
...data,
params: [...ports, ...data.params]
};
};
const attachEvents = that => {
const handleMessage = (...args) => {
const data = that.getData(...args);
that.dispatchEvent(new MessageEvent('message', {
data
}));
};
that.onMessage(handleMessage);
const handleClose = event => {
that.dispatchEvent(new Event('close'));
};
that.onClose(handleClose);
};
class Ipc extends EventTarget {
constructor(rawIpc) {
super();
this._rawIpc = rawIpc;
attachEvents(this);
}
}
const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
const NewLine$1 = '\n';
const joinLines$1 = lines => {
return lines.join(NewLine$1);
};
const RE_AT = /^\s+at/;
const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
const isNormalStackLine = line => {
return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
};
const getDetails = lines => {
const index = lines.findIndex(isNormalStackLine);
if (index === -1) {
return {
actualMessage: joinLines$1(lines),
rest: []
};
}
let lastIndex = index - 1;
while (++lastIndex < lines.length) {
if (!isNormalStackLine(lines[lastIndex])) {
break;
}
}
return {
actualMessage: lines[index - 1],
rest: lines.slice(index, lastIndex)
};
};
const splitLines$1 = lines => {
return lines.split(NewLine$1);
};
const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
const isMessageCodeBlockStartIndex = line => {
return RE_MESSAGE_CODE_BLOCK_START.test(line);
};
const isMessageCodeBlockEndIndex = line => {
return RE_MESSAGE_CODE_BLOCK_END.test(line);
};
const getMessageCodeBlock = stderr => {
const lines = splitLines$1(stderr);
const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
const relevantLines = lines.slice(startIndex, endIndex);
const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
return relevantMessage;
};
const isModuleNotFoundMessage = line => {
return line.includes('[ERR_MODULE_NOT_FOUND]');
};
const getModuleNotFoundError = stderr => {
const lines = splitLines$1(stderr);
const messageIndex = lines.findIndex(isModuleNotFoundMessage);
const message = lines[messageIndex];
return {
message,
code: ERR_MODULE_NOT_FOUND
};
};
const isModuleNotFoundError = stderr => {
if (!stderr) {
return false;
}
return stderr.includes('ERR_MODULE_NOT_FOUND');
};
const isModulesSyntaxError = stderr => {
if (!stderr) {
return false;
}
return stderr.includes('SyntaxError: Cannot use import statement outside a module');
};
const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
const isUnhelpfulNativeModuleError = stderr => {
return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
};
const getNativeModuleErrorMessage = stderr => {
const message = getMessageCodeBlock(stderr);
return {
message: `Incompatible native node module: ${message}`,
code: E_INCOMPATIBLE_NATIVE_MODULE
};
};
const getModuleSyntaxError = () => {
return {
message: `ES Modules are not supported in electron`,
code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
};
};
const getHelpfulChildProcessError = (stdout, stderr) => {
if (isUnhelpfulNativeModuleError(stderr)) {
return getNativeModuleErrorMessage(stderr);
}
if (isModulesSyntaxError(stderr)) {
return getModuleSyntaxError();
}
if (isModuleNotFoundError(stderr)) {
return getModuleNotFoundError(stderr);
}
const lines = splitLines$1(stderr);
const {
actualMessage,
rest
} = getDetails(lines);
return {
message: actualMessage,
code: '',
stack: rest
};
};
class IpcError extends VError {
// @ts-ignore
constructor(betterMessage, stdout = '', stderr = '') {
if (stdout || stderr) {
// @ts-ignore
const {
message,
code,
stack
} = getHelpfulChildProcessError(stdout, stderr);
const cause = new Error(message);
// @ts-ignore
cause.code = code;
cause.stack = stack;
super(cause, betterMessage);
} else {
super(betterMessage);
}
// @ts-ignore
this.name = 'IpcError';
// @ts-ignore
this.stdout = stdout;
// @ts-ignore
this.stderr = stderr;
}
}
const listen$b = ({
messagePort
}) => {
if (!isMessagePortMain(messagePort)) {
throw new IpcError('port must be of type MessagePortMain');
}
return messagePort;
};
const signal$c = messagePort => {
messagePort.start();
};
class IpcChildWithElectronMessagePort extends Ipc {
getData = getActualDataElectron;
send(message) {
this._rawIpc.postMessage(message);
}
sendAndTransfer(message) {
const {
newValue,
transfer
} = fixElectronParameters(message);
this._rawIpc.postMessage(newValue, transfer);
}
dispose() {
this._rawIpc.close();
}
onMessage(callback) {
this._rawIpc.on('message', callback);
}
onClose(callback) {
this._rawIpc.on('close', callback);
}
}
const wrap$j = messagePort => {
return new IpcChildWithElectronMessagePort(messagePort);
};
const IpcChildWithElectronMessagePort$1 = {
__proto__: null,
listen: listen$b,
signal: signal$c,
wrap: wrap$j
};
// @ts-ignore
const getUtilityProcessPortData = event => {
const {
data,
ports
} = event;
if (ports.length === 0) {
return data;
}
return {
...data,
params: [...ports, ...data.params]
};
};
const readyMessage = 'ready';
const listen$a = () => {
// @ts-ignore
const {
parentPort
} = process;
if (!parentPort) {
throw new Error('parent port must be defined');
}
return parentPort;
};
const signal$b = parentPort => {
parentPort.postMessage(readyMessage);
};
class IpcChildWithElectronUtilityProcess extends Ipc {
getData(event) {
return getUtilityProcessPortData(event);
}
send(message) {
this._rawIpc.postMessage(message);
}
sendAndTransfer(message) {
const {
newValue,
transfer
} = fixElectronParameters(message);
this._rawIpc.postMessage(newValue, transfer);
}
dispose() {
this._rawIpc.close();
}
onClose(callback) {
this._rawIpc.on('close', callback);
}
onMessage(callback) {
this._rawIpc.on('message', callback);
}
}
const wrap$i = parentPort => {
return new IpcChildWithElectronUtilityProcess(parentPort);
};
const IpcChildWithElectronUtilityProcess$1 = {
__proto__: null,
listen: listen$a,
signal: signal$b,
wrap: wrap$i
};
const getActualData = (message, handle) => {
if (handle) {
return {
...message,
params: [handle, ...message.params]
};
}
return message;
};
const getTransferrablesNode = value => {
const transferrables = getTransferrables(value);
if (transferrables.length === 0) {
throw new Error(`no transferrables found`);
}
return transferrables[0];
};
const listen$5 = async () => {
if (!process.send) {
throw new Error('process.send must be defined');
}
return process;
};
const signal$7 = process => {
process.send(readyMessage);
};
class IpcChildWithNodeForkedProcess extends Ipc {
getData(message, handle) {
return getActualData(message, handle);
}
onClose(callback) {
this._rawIpc.on('close', callback);
}
send(message) {
this._rawIpc.send(message);
}
onMessage(callback) {
this._rawIpc.on('message', callback);
}
sendAndTransfer(message) {
const transfer = getTransferrablesNode(message);
this._rawIpc.send(message, transfer);
}
dispose() {
// ignore
}
}
const wrap$d = process => {
return new IpcChildWithNodeForkedProcess(process);
};
const IpcChildWithNodeForkedProcess$1 = {
__proto__: null,
listen: listen$5,
signal: signal$7,
wrap: wrap$d
};
const listen$3 = async () => {
const {
parentPort
} = await import('node:worker_threads');
if (!parentPort) {
throw new IpcError('parentPort is required for node worker threads ipc');
}
return parentPort;
};
const signal$5 = parentPort => {
parentPort.postMessage(readyMessage);
};
class IpcChildWithNodeWorker extends Ipc {
getData(data) {
return data;
}
onClose(callback) {
this._rawIpc.on('close', callback);
}
send(message) {
this._rawIpc.postMessage(message);
}
onMessage(callback) {
this._rawIpc.on('message', callback);
}
sendAndTransfer(message) {
const transfer = getTransferrablesNode(message);
this._rawIpc.postMessage(message, transfer);
}
dispose() {
this._rawIpc.close();
}
}
const wrap$b = parentPort => {
return new IpcChildWithNodeWorker(parentPort);
};
const IpcChildWithNodeWorker$1 = {
__proto__: null,
listen: listen$3,
signal: signal$5,
wrap: wrap$b
};
const Open = 2;
const Close = 3;
const addListener = (emitter, type, callback) => {
if ('addEventListener' in emitter) {
emitter.addEventListener(type, callback);
} else {
emitter.on(type, callback);
}
};
const removeListener = (emitter, type, callback) => {
if ('removeEventListener' in emitter) {
emitter.removeEventListener(type, callback);
} else {
emitter.off(type, callback);
}
};
const getFirstEvent = (eventEmitter, eventMap) => {
const {
resolve,
promise
} = Promise.withResolvers();
const listenerMap = Object.create(null);
const cleanup = value => {
for (const event of Object.keys(eventMap)) {
removeListener(eventEmitter, event, listenerMap[event]);
}
resolve(value);
};
for (const [event, type] of Object.entries(eventMap)) {
const listener = event => {
cleanup({
type,
event
});
};
addListener(eventEmitter, event, listener);
listenerMap[event] = listener;
}
return promise;
};
// @ts-ignore
const getFirstWebSocketEvent = async webSocket => {
// @ts-ignore
const {
WebSocket
} = await import('ws');
switch (webSocket.readyState) {
case WebSocket.OPEN:
return {
type: Open,
event: undefined
};
case WebSocket.CLOSED:
return {
type: Close,
event: undefined
};
}
// @ts-ignore
const {
type,
event
} = await getFirstEvent(webSocket, {
open: Open,
close: Close
});
return {
type,
event
};
};
// @ts-ignore
const isWebSocketOpen = async webSocket => {
// @ts-ignore
const {
WebSocket
} = await import('ws');
return webSocket.readyState === WebSocket.OPEN;
};
// @ts-ignore
const serialize = message => {
return JSON.stringify(message);
};
// @ts-ignore
const deserialize = message => {
return JSON.parse(message.toString());
};
// @ts-ignore
const handleUpgrade$1 = async (...args) => {
const module = await Promise.resolve().then(function () { return index; });
// @ts-ignore
return module.handleUpgrade(...args);
};
const listen$1$1 = async ({
request,
handle
}) => {
if (!request) {
throw new IpcError('request must be defined');
}
if (!handle) {
throw new IpcError('handle must be defined');
}
const webSocket = await handleUpgrade$1(request, handle);
webSocket.pause();
if (!(await isWebSocketOpen(webSocket))) {
await getFirstWebSocketEvent(webSocket);
}
return webSocket;
};
const signal$4 = webSocket => {
webSocket.resume();
};
const wrap$9 = webSocket => {
return {
webSocket,
/**
* @type {any}
*/
wrappedListener: undefined,
// @ts-ignore
on(event, listener) {
switch (event) {
case 'message':
// @ts-ignore
const wrappedListener = message => {
const data = deserialize(message);
const event = {
data,
target: this
};
listener(event);
};
webSocket.on('message', wrappedListener);
break;
case 'close':
webSocket.on('close', listener);
break;
default:
throw new Error('unknown event listener type');
}
},
// @ts-ignore
off(event, listener) {
this.webSocket.off(event, listener);
},
// @ts-ignore
send(message) {
const stringifiedMessage = serialize(message);
this.webSocket.send(stringifiedMessage);
},
dispose() {
this.webSocket.close();
},
start() {
throw new Error('start method is deprecated');
}
};
};
const IpcChildWithWebSocket = {
__proto__: null,
listen: listen$1$1,
signal: signal$4,
wrap: wrap$9
};
const NodeWorker = 1;
const NodeForkedProcess = 2;
const ElectronUtilityProcess = 3;
const ElectronMessagePort = 4;
const WebSocket = 6;
const Auto = () => {
const {
argv
} = process;
if (argv.includes('--ipc-type=node-worker')) {
return NodeWorker;
}
if (argv.includes('--ipc-type=node-forked-process')) {
return NodeForkedProcess;
}
if (argv.includes('--ipc-type=electron-utility-process')) {
return ElectronUtilityProcess;
}
throw new Error(`[shared-process] unknown ipc type`);
};
const getModule$2 = method => {
switch (method) {
case NodeForkedProcess:
return IpcChildWithNodeForkedProcess$1;
case NodeWorker:
return IpcChildWithNodeWorker$1;
case ElectronUtilityProcess:
return IpcChildWithElectronUtilityProcess$1;
case ElectronMessagePort:
return IpcChildWithElectronMessagePort$1;
case WebSocket:
return IpcChildWithWebSocket;
default:
throw new Error('unexpected ipc type');
}
};
const listen$1 = async ({
method,
...params
}) => {
const module = getModule$2(method);
// @ts-ignore
const rawIpc = await module.listen(params);
// @ts-ignore
if (module.signal) {
// @ts-ignore
module.signal(rawIpc);
}
// @ts-ignore
const ipc = module.wrap(rawIpc);
return ipc;
};
const targetMessagePort = async (messagePort, message) => {
object(messagePort);
const ipc = await listen$1({
method: ElectronMessagePort,
messagePort
});
return ipc;
};
const upgradeMessagePort = () => {
return {
type: 'handle'
};
};
const HandleIpcSharedProcess = {
__proto__: null,
targetMessagePort,
upgradeMessagePort
};
const SharedProcess = 1;
const getModule$1 = ipcId => {
number(ipcId);
switch (ipcId) {
case SharedProcess:
return HandleIpcSharedProcess;
default:
throw new Error(`unexpected incoming ipc`);
}
};
const getIpcAndResponse = (module, handle, message) => {
return handleIncomingIpcMessagePort(module, handle, message);
};
const handleIncomingIpc = async (ipcId, handle, message) => {
number(ipcId);
const module = getModule$1(ipcId);
const {
target,
response
} = await getIpcAndResponse(module, handle, message);
await applyIncomingIpcResponse(target, response);
};
const handleElectronMessagePort = async (messagePort, ipcId) => {
object(messagePort);
// TODO use ipcId parameter
return handleIncomingIpc(SharedProcess, messagePort, {});
};
const openFolder = async path => {
try {
const {
default: open
} = await import('open');
await open(path);
} catch (error) {
throw new VError(error, `Failed to open ${path}`);
}
};
const __dirname = dirname$1(fileURLToPath(import.meta.url));
const root = resolve$1(__dirname, '../../../../../');
const getPtyHostPath = () => {
return join(root, '@lvce-editor', 'pty-host');
};
const isElectron = () => {
return Boolean(process.env.ELECTRON_RUN_AS_NODE) || 'electron' in process.versions;
};
const getModule = async () => {
if (isElectron()) {
return Promise.resolve().then(function () { return RebuildForElectron; });
}
return Promise.resolve().then(function () { return RebuildForNode; });
};
const rebuildNodePty = async () => {
try {
const ptyHostPath = getPtyHostPath();
const module = await getModule();
await module.rebuild(ptyHostPath);
} catch (error) {
throw new VError(error, `Failed to rebuild node-pty`);
}
};
const createSymLink = async (target, path) => {
const {
default: symlinkDir
} = await import('symlink-dir');
await symlinkDir(target, path);
};
const getTmpFile = async () => {
const {
file
} = await import('tmp-promise');
const {
path
} = await file({
prefix: 'lvce-extension-'
});
return path;
};
const getTmpDir = async () => {
const {
dir
} = await import('tmp-promise');
const {
path
} = await dir({
prefix: 'lvce-extension-'
});
return path;
};
const trash = async path => {
const {
default: _trash
} = await import('trash');
await _trash(path);
};
const commandMap = {
'ClipBoard.readFiles': readFiles$3,
'ClipBoard.writeFiles': writeFiles$3,
'Download.download': download,
'Download.downloadAndExtractTarGz': downloadAndExtractTarGz,
'Download.getUrl': getUrl,
'Extract.extractTarBr': extractTarBr,
'Extract.extractTarGz': extractTarGz,
'OpenNativeFolder.openNativeFolder': openFolder,
'RebuildNodePty.rebuildNodePty': rebuildNodePty,
'Symlink.createSymLink': createSymLink,
'TmpFile.getTmpDir': getTmpDir,
'TmpFile.getTmpFile': getTmpFile,
'TrashNode.trash': trash,
'HandleElectronMessagePort.handleElectronMessagePort': handleElectronMessagePort
};
const listen = async () => {
const ipc = await listen$1({
method: Auto()
});
handleIpc(ipc);
};
const main = async () => {
registerCommands(commandMap);
await listen();
};
main();
const exec = async (command, args, options) => {
string(command);
array(args);
object(options);
const {
execa
} = await import('execa');
const {
stdout,
stderr
} = await execa(command, args, options);
return {
stdout: String(stdout),
stderr: String(stderr)
};
};
const NewLine = '\n';
const joinLines = lines => {
return lines.join(NewLine);
};
const splitLines = lines => {
return lines.split(NewLine);
};
const removePrefix = file => {
if (file.startsWith('file://')) {
return file.slice('file://'.length);
}
return file;
};
const addPrefix = file => {
return `file://${file}`;
};
const readFiles$2 = async () => {
let result;
try {
result = await exec('xclip', ['-selection', 'clipboard', '-o', '-t', 'x-special/gnome-copied-files'], {});
} catch (error) {
if (error &&
// @ts-ignore
error.stderr === 'Error: target x-special/gnome-copied-files not available') {
return;
}
throw error;
}
const [type, ...files] = splitLines(result.stdout);
const actualFiles = files.map(removePrefix);
return {
source: 'gnomeCopiedFiles',
type,
files: actualFiles
};
};
const writeFiles$2 = async (type, files) => {
const filesWithPrefix = files.map(addPrefix);
const gnomeCopiedFilesContent = joinLines([type, ...filesWithPrefix]);
const uriListContent = joinLines(filesWithPrefix);
const plainContent = joinLines(files);
try {
await exec('xclip', ['-i', '-selection', 'clipboard', '-t', 'x-special/gnome-copied-files'], {
input: gnomeCopiedFilesContent
});
await exec('xclip', ['-i', '-selection', 'clipboard', '-t', 'text/uri-list'], {
input: uriListContent
});
await exec('xclip', ['-i', '-selection', 'clipboard', '-t', 'text/plain'], {
input: plainContent
});
} catch (error) {
throw new VError(error, 'Failed to copy files to clipboard');
}
};
const ClipBoardGnome = {
__proto__: null,
readFiles: readFiles$2,
writeFiles: writeFiles$2
};
const readFiles$1 = async () => {
// @ts-ignore
const clipboardEx = await import('electron-clipboard-ex');
const filePaths = clipboardEx.readFilePaths();
console.log(filePaths);
return {
source: 'electron-clipboard-ex',
type: 'copy',
// TODO can't be sure on this
files: filePaths
};
};
const writeFiles$1 = async (type, files) => {
try {
// @ts-ignore
const clipboardEx = await import('electron-clipboard-ex');
clipboardEx.writeFilePaths(files);
} catch (error) {
throw new VError(error, 'Failed to copy files to clipboard');
}
};
const ClipBoardWindows = {
__proto__: null,
readFiles: readFiles$1,
writeFiles: writeFiles$1
};
const readFiles = async () => {
return {
source: 'notSupported',
type: 'none',
files: []
};
};
const writeFiles = (type, files) => {
const desktop = getDesktop();
console.info(`writing files to clipboard is not yet supported on ${desktop}`);
};
const ClipBoardNoop = {
__proto__: null,
readFiles,
writeFiles
};
const createWebSocketServer = async () => {
const _ws = await import('ws');
// workaround for jest or node bug
const WebSocketServer = _ws.WebSocketServer ? _ws.WebSocketServer :
// @ts-ignore
_ws.default.WebSocketServer;
const webSocketServer = new WebSocketServer({
noServer: true
// TODO not sure if ws compress is working at all
// perMessageDeflate: true
});
return webSocketServer;
};
const doSocketUpgrade = (webSocketServer, request, socket) => {
const {
promise,
resolve
} = Promise.withResolvers();
webSocketServer.handleUpgrade(request, socket, Buffer.alloc(0), resolve);
return promise;
};
const handleUpgrade = async (request, socket) => {
const webSocketServer = await createWebSocketServer();
const webSocket = await doSocketUpgrade(webSocketServer, request, socket);
return webSocket;
};
const index = {
__proto__: null,
handleUpgrade
};
const isWindows = process.platform === 'win32';
const getElectronRebuildPath = () => {
if (isWindows) {
return join(root, '..', '..', '..', 'packages', 'main-process', 'node_modules', '.bin', 'electron-rebuild.cmd');
}
return join(root, '..', '..', '..', 'packages', 'main-process', 'node_modules', '.bin', 'electron-rebuild');
};
const rebuild$1 = async cwd => {
try {
const electronRebuildPath = getElectronRebuildPath();
await exec(electronRebuildPath, [], {
cwd,
stdio: 'inherit'
});
} catch (error) {
throw new VError(error, `Failed to rebuild native module`);
}
};
const RebuildForElectron = {
__proto__: null,
rebuild: rebuild$1
};
const rebuild = async cwd => {
try {
await exec('npm', ['rebuild'], {
cwd,
stdio: 'inherit'
});
} catch (error) {
throw new VError(error, `Failed to rebuild native module`);
}
};
const RebuildForNode = {
__proto__: null,
rebuild
};