mock-violentmonkey
Version:
Mock violentmonkey's globals for testing userscripts
65 lines • 2.42 kB
JavaScript
import { Buffer } from 'node:buffer';
import { VMStorage } from '../vm-storage.js';
import { GM_xmlhttpRequest } from './xmlhttprequest.js';
const downloads = new VMStorage(() => new Map());
const download = (options, name) => {
const options_ = typeof options === 'string'
? {
url: options,
name: name,
}
: options;
const { url, name: name_ } = options_;
if (typeof url !== 'string') {
throw new TypeError('Expected url to be a string.');
}
if (typeof name_ !== 'string') {
throw new TypeError('Expected name to be a string.');
}
// This is a fix for a race condition where onloadend is called before onload
// Because all event handlers but onload were passed without any modification
// this caused a race condition
// Our onload was called. Because it is async, onloadend was called, before we could call options_.onload
// To fix this, we intercept that call and call onloadend when we're ready
// For errors, we call it right away
// For onload, we wait until the buffer is loaded and then call their onload and then onloadend
function overrideError(name) {
return function (response) {
void options_[`on${name}`]?.(response);
void options_.onloadend?.(response);
};
}
GM_xmlhttpRequest({
...options_,
url,
responseType: 'blob',
async onload(response) {
const blob = response.response;
const buffer = Buffer.from(await blob.arrayBuffer());
downloads.get(true).set(name_, buffer);
void options_.onload?.(response);
void options_.onloadend?.(response);
},
onloadend() { },
onerror: overrideError('error'),
onabort: overrideError('abort'),
ontimeout: overrideError('timeout'),
});
};
const getDownloads = () => {
const downloadsMap = downloads.get(false);
const result = {};
for (const [name, buffer] of downloadsMap ?? []) {
result[name] = Buffer.from(buffer);
}
return result;
};
const getDownload = (name) => {
const buffer = downloads.get(false)?.get(name);
return buffer && Buffer.from(buffer);
};
export { download as GM_download, getDownloads, getDownload };
Object.defineProperty(globalThis, 'GM_download', {
value: download,
});
//# sourceMappingURL=download.js.map