Compare commits
No commits in common. "6d5fb27c999c4a7e98b6febee8f9c9476dfbe930" and "634165091667e159a25cfbda65e61611aad4104e" have entirely different histories.
6d5fb27c99
...
6341650916
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "real-debrid-downloader",
|
"name": "real-debrid-downloader",
|
||||||
"version": "1.6.71",
|
"version": "1.6.70",
|
||||||
"description": "Desktop downloader",
|
"description": "Desktop downloader",
|
||||||
"main": "build/main/main/main.js",
|
"main": "build/main/main/main.js",
|
||||||
"author": "Sucukdeluxe",
|
"author": "Sucukdeluxe",
|
||||||
|
|||||||
@ -44,7 +44,6 @@ export function defaultSettings(): AppSettings {
|
|||||||
realDebridUseWebLogin: false,
|
realDebridUseWebLogin: false,
|
||||||
megaLogin: "",
|
megaLogin: "",
|
||||||
megaPassword: "",
|
megaPassword: "",
|
||||||
megaDebridPreferApi: true,
|
|
||||||
bestToken: "",
|
bestToken: "",
|
||||||
allDebridToken: "",
|
allDebridToken: "",
|
||||||
allDebridUseWebLogin: false,
|
allDebridUseWebLogin: false,
|
||||||
|
|||||||
@ -12,8 +12,6 @@ const BEST_DEBRID_API_BASE = "https://bestdebrid.com/api/v1";
|
|||||||
const ALL_DEBRID_API_BASE = "https://api.alldebrid.com/v4";
|
const ALL_DEBRID_API_BASE = "https://api.alldebrid.com/v4";
|
||||||
const ALL_DEBRID_API_BASE_V41 = "https://api.alldebrid.com/v4.1";
|
const ALL_DEBRID_API_BASE_V41 = "https://api.alldebrid.com/v4.1";
|
||||||
|
|
||||||
const MEGA_DEBRID_API_BASE = "https://www.mega-debrid.eu/api.php";
|
|
||||||
|
|
||||||
const ONEFICHIER_API_BASE = "https://api.1fichier.com/v1";
|
const ONEFICHIER_API_BASE = "https://api.1fichier.com/v1";
|
||||||
const ONEFICHIER_URL_RE = /^https?:\/\/(?:www\.)?(?:1fichier\.com|alterupload\.com|cjoint\.net|desfichiers\.com|dfichiers\.com|megadl\.fr|mesfichiers\.org|piecejointe\.net|pjointe\.com|tenvoi\.com|dl4free\.com)\/\?([a-z0-9]{5,20})$/i;
|
const ONEFICHIER_URL_RE = /^https?:\/\/(?:www\.)?(?:1fichier\.com|alterupload\.com|cjoint\.net|desfichiers\.com|dfichiers\.com|megadl\.fr|mesfichiers\.org|piecejointe\.net|pjointe\.com|tenvoi\.com|dl4free\.com)\/\?([a-z0-9]{5,20})$/i;
|
||||||
|
|
||||||
@ -161,14 +159,6 @@ function parseJson(text: string): unknown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseJsonSafe(text: string): Record<string, unknown> | null {
|
|
||||||
const parsed = parseJson(text);
|
|
||||||
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return parsed as Record<string, unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function pickString(payload: Record<string, unknown> | null, keys: string[]): string {
|
function pickString(payload: Record<string, unknown> | null, keys: string[]): string {
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
return "";
|
return "";
|
||||||
@ -669,105 +659,11 @@ function buildBestDebridRequests(link: string, token: string): BestDebridRequest
|
|||||||
class MegaDebridClient {
|
class MegaDebridClient {
|
||||||
private megaWebUnrestrict?: MegaWebUnrestrictor;
|
private megaWebUnrestrict?: MegaWebUnrestrictor;
|
||||||
|
|
||||||
private login: string;
|
public constructor(megaWebUnrestrict?: MegaWebUnrestrictor) {
|
||||||
|
|
||||||
private password: string;
|
|
||||||
|
|
||||||
private preferApi: boolean;
|
|
||||||
|
|
||||||
private static cachedApiToken = "";
|
|
||||||
|
|
||||||
private static cachedApiTokenAt = 0;
|
|
||||||
|
|
||||||
public constructor(login: string, password: string, preferApi: boolean, megaWebUnrestrict?: MegaWebUnrestrictor) {
|
|
||||||
this.login = login;
|
|
||||||
this.password = password;
|
|
||||||
this.preferApi = preferApi;
|
|
||||||
this.megaWebUnrestrict = megaWebUnrestrict;
|
this.megaWebUnrestrict = megaWebUnrestrict;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async connectApi(signal?: AbortSignal): Promise<string | null> {
|
public async unrestrictLink(link: string, signal?: AbortSignal): Promise<UnrestrictedLink> {
|
||||||
// Return cached token if fresh (max 20 min)
|
|
||||||
if (MegaDebridClient.cachedApiToken && Date.now() - MegaDebridClient.cachedApiTokenAt < 20 * 60 * 1000) {
|
|
||||||
return MegaDebridClient.cachedApiToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${MEGA_DEBRID_API_BASE}?action=connectUser&login=${encodeURIComponent(this.login)}&password=${encodeURIComponent(this.password)}`;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
headers: { "User-Agent": DEBRID_USER_AGENT },
|
|
||||||
signal: withTimeoutSignal(signal, API_TIMEOUT_MS)
|
|
||||||
});
|
|
||||||
const text = await response.text();
|
|
||||||
if (!response.ok) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const payload = parseJsonSafe(text);
|
|
||||||
if (!payload || payload.response_code !== "ok") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const token = String(payload.token || "").trim();
|
|
||||||
if (!token) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
MegaDebridClient.cachedApiToken = token;
|
|
||||||
MegaDebridClient.cachedApiTokenAt = Date.now();
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async unrestrictViaApi(link: string, signal?: AbortSignal): Promise<UnrestrictedLink | null> {
|
|
||||||
const token = await this.connectApi(signal);
|
|
||||||
if (!token) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${MEGA_DEBRID_API_BASE}?action=getLink&token=${encodeURIComponent(token)}`;
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
"User-Agent": DEBRID_USER_AGENT
|
|
||||||
},
|
|
||||||
body: new URLSearchParams({ link }),
|
|
||||||
signal: withTimeoutSignal(signal, API_TIMEOUT_MS)
|
|
||||||
});
|
|
||||||
const text = await response.text();
|
|
||||||
if (!response.ok) {
|
|
||||||
// Token might be invalid, clear cache
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
MegaDebridClient.cachedApiToken = "";
|
|
||||||
MegaDebridClient.cachedApiTokenAt = 0;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const payload = parseJsonSafe(text);
|
|
||||||
if (!payload || payload.response_code !== "ok") {
|
|
||||||
// Token expired — clear cache for next attempt
|
|
||||||
if (payload && String(payload.response_code || "").includes("token")) {
|
|
||||||
MegaDebridClient.cachedApiToken = "";
|
|
||||||
MegaDebridClient.cachedApiTokenAt = 0;
|
|
||||||
}
|
|
||||||
const errorText = String(payload?.response_text || "").trim();
|
|
||||||
if (errorText) {
|
|
||||||
throw new Error(`Mega-Debrid API: ${errorText}`);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const directUrl = String(payload.debridLink || "").trim();
|
|
||||||
if (!directUrl) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const fileName = String(payload.filename || "").trim() || filenameFromUrl(directUrl) || filenameFromUrl(link);
|
|
||||||
return {
|
|
||||||
directUrl,
|
|
||||||
fileName,
|
|
||||||
fileSize: null,
|
|
||||||
retriesUsed: 0,
|
|
||||||
sourceLabel: "API"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async unrestrictViaWeb(link: string, signal?: AbortSignal): Promise<UnrestrictedLink> {
|
|
||||||
if (!this.megaWebUnrestrict) {
|
if (!this.megaWebUnrestrict) {
|
||||||
throw new Error("Mega-Web-Fallback nicht verfügbar");
|
throw new Error("Mega-Web-Fallback nicht verfügbar");
|
||||||
}
|
}
|
||||||
@ -785,7 +681,6 @@ class MegaDebridClient {
|
|||||||
}
|
}
|
||||||
if (web?.directUrl) {
|
if (web?.directUrl) {
|
||||||
web.retriesUsed = attempt - 1;
|
web.retriesUsed = attempt - 1;
|
||||||
web.sourceLabel = "Web";
|
|
||||||
return web;
|
return web;
|
||||||
}
|
}
|
||||||
if (web && !web.directUrl) {
|
if (web && !web.directUrl) {
|
||||||
@ -804,29 +699,6 @@ class MegaDebridClient {
|
|||||||
}
|
}
|
||||||
throw new Error(String(lastError || "Mega-Web Unrestrict fehlgeschlagen").replace(/^Error:\s*/i, ""));
|
throw new Error(String(lastError || "Mega-Web Unrestrict fehlgeschlagen").replace(/^Error:\s*/i, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async unrestrictLink(link: string, signal?: AbortSignal): Promise<UnrestrictedLink> {
|
|
||||||
if (this.preferApi && this.login.trim() && this.password.trim()) {
|
|
||||||
// API mode: try API first, fall back to web on failure
|
|
||||||
try {
|
|
||||||
const apiResult = await this.unrestrictViaApi(link, signal);
|
|
||||||
if (apiResult) {
|
|
||||||
logger.info(`Mega-Debrid (API) unrestrict OK: ${apiResult.fileName}`);
|
|
||||||
return apiResult;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const errorText = compactErrorText(error);
|
|
||||||
if (signal?.aborted || (/aborted/i.test(errorText) && !/timeout/i.test(errorText))) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
logger.warn(`Mega-Debrid API fehlgeschlagen, versuche Web-Fallback: ${errorText}`);
|
|
||||||
}
|
|
||||||
return this.unrestrictViaWeb(link, signal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Web mode only
|
|
||||||
return this.unrestrictViaWeb(link, signal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BestDebridClient {
|
class BestDebridClient {
|
||||||
@ -1582,7 +1454,7 @@ export class DebridService {
|
|||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
provider: "onefichier",
|
provider: "onefichier",
|
||||||
providerLabel: PROVIDER_LABELS["onefichier"] + (result.sourceLabel ? ` (${result.sourceLabel})` : "")
|
providerLabel: PROVIDER_LABELS["onefichier"]
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorText = compactErrorText(error);
|
const errorText = compactErrorText(error);
|
||||||
@ -1602,7 +1474,7 @@ export class DebridService {
|
|||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
provider: "ddownload",
|
provider: "ddownload",
|
||||||
providerLabel: PROVIDER_LABELS["ddownload"] + (result.sourceLabel ? ` (${result.sourceLabel})` : "")
|
providerLabel: PROVIDER_LABELS["ddownload"]
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorText = compactErrorText(error);
|
const errorText = compactErrorText(error);
|
||||||
@ -1637,7 +1509,7 @@ export class DebridService {
|
|||||||
...result,
|
...result,
|
||||||
fileName,
|
fileName,
|
||||||
provider: primary,
|
provider: primary,
|
||||||
providerLabel: PROVIDER_LABELS[primary] + (result.sourceLabel ? ` (${result.sourceLabel})` : "")
|
providerLabel: PROVIDER_LABELS[primary]
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorText = compactErrorText(error);
|
const errorText = compactErrorText(error);
|
||||||
@ -1670,7 +1542,7 @@ export class DebridService {
|
|||||||
...result,
|
...result,
|
||||||
fileName,
|
fileName,
|
||||||
provider,
|
provider,
|
||||||
providerLabel: PROVIDER_LABELS[provider] + (result.sourceLabel ? ` (${result.sourceLabel})` : "")
|
providerLabel: PROVIDER_LABELS[provider]
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorText = compactErrorText(error);
|
const errorText = compactErrorText(error);
|
||||||
@ -1693,7 +1565,7 @@ export class DebridService {
|
|||||||
return Boolean(this.shouldUseRealDebridWeb(settings) || settings.token.trim());
|
return Boolean(this.shouldUseRealDebridWeb(settings) || settings.token.trim());
|
||||||
}
|
}
|
||||||
if (provider === "megadebrid") {
|
if (provider === "megadebrid") {
|
||||||
return Boolean(settings.megaLogin.trim() && settings.megaPassword.trim());
|
return Boolean(settings.megaLogin.trim() && settings.megaPassword.trim() && this.options.megaWebUnrestrict);
|
||||||
}
|
}
|
||||||
if (provider === "alldebrid") {
|
if (provider === "alldebrid") {
|
||||||
return Boolean(this.shouldUseAllDebridWeb(settings) || settings.allDebridToken.trim());
|
return Boolean(this.shouldUseAllDebridWeb(settings) || settings.allDebridToken.trim());
|
||||||
@ -1719,7 +1591,7 @@ export class DebridService {
|
|||||||
return new RealDebridClient(settings.token).unrestrictLink(link, signal);
|
return new RealDebridClient(settings.token).unrestrictLink(link, signal);
|
||||||
}
|
}
|
||||||
if (provider === "megadebrid") {
|
if (provider === "megadebrid") {
|
||||||
return new MegaDebridClient(settings.megaLogin, settings.megaPassword, settings.megaDebridPreferApi, this.options.megaWebUnrestrict).unrestrictLink(link, signal);
|
return new MegaDebridClient(this.options.megaWebUnrestrict).unrestrictLink(link, signal);
|
||||||
}
|
}
|
||||||
if (provider === "alldebrid") {
|
if (provider === "alldebrid") {
|
||||||
if (this.shouldUseAllDebridWeb(settings) && this.options.allDebridWebUnrestrict) {
|
if (this.shouldUseAllDebridWeb(settings) && this.options.allDebridWebUnrestrict) {
|
||||||
|
|||||||
@ -9,7 +9,6 @@ export interface UnrestrictedLink {
|
|||||||
fileSize: number | null;
|
fileSize: number | null;
|
||||||
retriesUsed: number;
|
retriesUsed: number;
|
||||||
skipTlsVerify?: boolean;
|
skipTlsVerify?: boolean;
|
||||||
sourceLabel?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldRetryStatus(status: number): boolean {
|
function shouldRetryStatus(status: number): boolean {
|
||||||
|
|||||||
@ -110,7 +110,6 @@ export function normalizeSettings(settings: AppSettings): AppSettings {
|
|||||||
realDebridUseWebLogin: Boolean(settings.realDebridUseWebLogin),
|
realDebridUseWebLogin: Boolean(settings.realDebridUseWebLogin),
|
||||||
megaLogin: asText(settings.megaLogin),
|
megaLogin: asText(settings.megaLogin),
|
||||||
megaPassword: asText(settings.megaPassword),
|
megaPassword: asText(settings.megaPassword),
|
||||||
megaDebridPreferApi: settings.megaDebridPreferApi !== undefined ? Boolean(settings.megaDebridPreferApi) : true,
|
|
||||||
bestToken: asText(settings.bestToken),
|
bestToken: asText(settings.bestToken),
|
||||||
allDebridToken: asText(settings.allDebridToken),
|
allDebridToken: asText(settings.allDebridToken),
|
||||||
allDebridUseWebLogin: Boolean(settings.allDebridUseWebLogin),
|
allDebridUseWebLogin: Boolean(settings.allDebridUseWebLogin),
|
||||||
|
|||||||
@ -63,7 +63,7 @@ const emptyStats = (): DownloadStats => ({
|
|||||||
|
|
||||||
const emptySnapshot = (): UiSnapshot => ({
|
const emptySnapshot = (): UiSnapshot => ({
|
||||||
settings: {
|
settings: {
|
||||||
token: "", realDebridUseWebLogin: false, megaLogin: "", megaPassword: "", megaDebridPreferApi: true, bestToken: "", allDebridToken: "", allDebridUseWebLogin: false, ddownloadLogin: "", ddownloadPassword: "", oneFichierApiKey: "",
|
token: "", realDebridUseWebLogin: false, megaLogin: "", megaPassword: "", bestToken: "", allDebridToken: "", allDebridUseWebLogin: false, ddownloadLogin: "", ddownloadPassword: "", oneFichierApiKey: "",
|
||||||
archivePasswordList: "",
|
archivePasswordList: "",
|
||||||
rememberToken: true, providerPrimary: "realdebrid", providerSecondary: "megadebrid",
|
rememberToken: true, providerPrimary: "realdebrid", providerSecondary: "megadebrid",
|
||||||
providerTertiary: "bestdebrid", autoProviderFallback: true, outputDir: "", packageName: "",
|
providerTertiary: "bestdebrid", autoProviderFallback: true, outputDir: "", packageName: "",
|
||||||
@ -2841,7 +2841,6 @@ export function App(): ReactElement {
|
|||||||
<input value={settingsDraft.megaLogin} onChange={(e) => setText("megaLogin", e.target.value)} />
|
<input value={settingsDraft.megaLogin} onChange={(e) => setText("megaLogin", e.target.value)} />
|
||||||
<label>Mega-Debrid Passwort</label>
|
<label>Mega-Debrid Passwort</label>
|
||||||
<input type="password" value={settingsDraft.megaPassword} onChange={(e) => setText("megaPassword", e.target.value)} />
|
<input type="password" value={settingsDraft.megaPassword} onChange={(e) => setText("megaPassword", e.target.value)} />
|
||||||
<label className="toggle-line"><input type="checkbox" checked={settingsDraft.megaDebridPreferApi} onChange={(e) => setBool("megaDebridPreferApi", e.target.checked)} /> Mega-Debrid bevorzugt über API (schneller, Fallback auf Web)</label>
|
|
||||||
<label>BestDebrid API Token</label>
|
<label>BestDebrid API Token</label>
|
||||||
<input type="password" value={settingsDraft.bestToken} onChange={(e) => setText("bestToken", e.target.value)} />
|
<input type="password" value={settingsDraft.bestToken} onChange={(e) => setText("bestToken", e.target.value)} />
|
||||||
<label>AllDebrid API Key</label>
|
<label>AllDebrid API Key</label>
|
||||||
|
|||||||
@ -41,7 +41,6 @@ export interface AppSettings {
|
|||||||
realDebridUseWebLogin: boolean;
|
realDebridUseWebLogin: boolean;
|
||||||
megaLogin: string;
|
megaLogin: string;
|
||||||
megaPassword: string;
|
megaPassword: string;
|
||||||
megaDebridPreferApi: boolean;
|
|
||||||
bestToken: string;
|
bestToken: string;
|
||||||
allDebridToken: string;
|
allDebridToken: string;
|
||||||
allDebridUseWebLogin: boolean;
|
allDebridUseWebLogin: boolean;
|
||||||
|
|||||||
@ -397,11 +397,11 @@ describe("debrid service", () => {
|
|||||||
expect(realDebridWeb).not.toHaveBeenCalled();
|
expect(realDebridWeb).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("treats MegaDebrid as not configured when no credentials are set", async () => {
|
it("treats MegaDebrid as not configured when web fallback callback is unavailable", async () => {
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings(),
|
...defaultSettings(),
|
||||||
megaLogin: "",
|
megaLogin: "user",
|
||||||
megaPassword: "",
|
megaPassword: "pass",
|
||||||
providerPrimary: "megadebrid" as const,
|
providerPrimary: "megadebrid" as const,
|
||||||
providerSecondary: "none" as const,
|
providerSecondary: "none" as const,
|
||||||
providerTertiary: "none" as const,
|
providerTertiary: "none" as const,
|
||||||
@ -412,7 +412,7 @@ describe("debrid service", () => {
|
|||||||
await expect(service.unrestrictLink("https://rapidgator.net/file/missing-mega-web")).rejects.toThrow(/nicht konfiguriert/i);
|
await expect(service.unrestrictLink("https://rapidgator.net/file/missing-mega-web")).rejects.toThrow(/nicht konfiguriert/i);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses Mega web fallback when API fails", async () => {
|
it("uses Mega web path exclusively", async () => {
|
||||||
const settings = {
|
const settings = {
|
||||||
...defaultSettings(),
|
...defaultSettings(),
|
||||||
token: "",
|
token: "",
|
||||||
@ -426,7 +426,6 @@ describe("debrid service", () => {
|
|||||||
autoProviderFallback: true
|
autoProviderFallback: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// API returns 404 for connectUser → API fails, falls back to web
|
|
||||||
const fetchSpy = vi.fn(async () => new Response("not-found", { status: 404 }));
|
const fetchSpy = vi.fn(async () => new Response("not-found", { status: 404 }));
|
||||||
globalThis.fetch = fetchSpy as unknown as typeof fetch;
|
globalThis.fetch = fetchSpy as unknown as typeof fetch;
|
||||||
|
|
||||||
@ -442,6 +441,7 @@ describe("debrid service", () => {
|
|||||||
expect(result.provider).toBe("megadebrid");
|
expect(result.provider).toBe("megadebrid");
|
||||||
expect(result.directUrl).toContain("unrestrict.link/download/file/");
|
expect(result.directUrl).toContain("unrestrict.link/download/file/");
|
||||||
expect(megaWeb).toHaveBeenCalledTimes(1);
|
expect(megaWeb).toHaveBeenCalledTimes(1);
|
||||||
|
expect(fetchSpy).toHaveBeenCalledTimes(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("aborts Mega web unrestrict when caller signal is cancelled", async () => {
|
it("aborts Mega web unrestrict when caller signal is cancelled", async () => {
|
||||||
@ -458,9 +458,6 @@ describe("debrid service", () => {
|
|||||||
autoProviderFallback: false
|
autoProviderFallback: false
|
||||||
};
|
};
|
||||||
|
|
||||||
// API connect fails fast → falls through to web fallback
|
|
||||||
globalThis.fetch = (async () => new Response("error", { status: 500 })) as typeof fetch;
|
|
||||||
|
|
||||||
const megaWeb = vi.fn((_link: string, signal?: AbortSignal): Promise<never> => new Promise((_, reject) => {
|
const megaWeb = vi.fn((_link: string, signal?: AbortSignal): Promise<never> => new Promise((_, reject) => {
|
||||||
const onAbort = (): void => reject(new Error("aborted:mega-web-test"));
|
const onAbort = (): void => reject(new Error("aborted:mega-web-test"));
|
||||||
if (signal?.aborted) {
|
if (signal?.aborted) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user