58 lines
2.3 KiB
JavaScript
58 lines
2.3 KiB
JavaScript
// A menu can outlive its MV3 service worker while the user runs the installer.
|
|
// Reopen the UI port on the next command; never replay requests already sent.
|
|
export function createUiConnection({ onEvent, onDisconnect, runtime = chrome.runtime }) {
|
|
const requests = new Map();
|
|
const disconnected = () => new Error("Связь с расширением прервана. Нажмите «Проверить подключение» или «Запустить» для повторного подключения.");
|
|
let port = null;
|
|
|
|
function drop(connection) {
|
|
if (port !== connection) return;
|
|
port = null;
|
|
for (const [id, request] of requests) {
|
|
if (request.port !== connection) continue;
|
|
requests.delete(id); request.reject(disconnected());
|
|
}
|
|
onDisconnect?.();
|
|
}
|
|
function connect() {
|
|
if (port) return port;
|
|
const connection = runtime.connect({ name: "ytdl-ui" });
|
|
port = connection;
|
|
connection.onMessage.addListener(message => {
|
|
if (port !== connection) return;
|
|
if (message.event) { onEvent?.(message); return; }
|
|
const request = requests.get(message.id);
|
|
if (!request || request.port !== connection) return;
|
|
requests.delete(message.id);
|
|
message.ok ? request.resolve(message.result) : request.reject(new Error(message.error));
|
|
});
|
|
connection.onDisconnect.addListener(() => {
|
|
// Read lastError in the callback to avoid an unchecked runtime error.
|
|
void runtime.lastError?.message;
|
|
drop(connection);
|
|
});
|
|
return connection;
|
|
}
|
|
function call(action, payload = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
let connection, id;
|
|
try {
|
|
connection = connect(); id = crypto.randomUUID();
|
|
requests.set(id, { resolve, reject, port: connection });
|
|
connection.postMessage({ id, action, payload });
|
|
return;
|
|
} catch (error) {
|
|
if (id) requests.delete(id);
|
|
if (connection) drop(connection);
|
|
// A synchronous disconnected-port error means this command was not sent.
|
|
if (attempt === 0 && /disconnected port/i.test(error.message)) continue;
|
|
reject(/disconnected port/i.test(error.message) ? disconnected() : error);
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return { call };
|
|
}
|