UNPKG

@lvce-editor/network-process

Version:
1,561 lines (1,493 loc) 37.9 kB
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 };