UNPKG

@provablehq/sdk

Version:

A Software Development Kit (SDK) for Zero-Knowledge Transactions

262 lines (256 loc) 8.83 kB
import 'core-js/proposals/json-parse-with-source.js'; import { webcrypto } from 'node:crypto'; import * as $fs from 'node:fs'; import $mime from 'mime/lite'; import $xmlhttprequest from 'xmlhttprequest-ssl'; import $request from 'sync-request'; import * as $worker from 'node:worker_threads'; import * as $os from 'node:os'; if (globalThis.crypto == null) { globalThis.crypto = webcrypto; } const oldFetch = globalThis.fetch; let supports = null; async function checkFetch() { try { await oldFetch(new URL("file:")); return true; } catch (e) { return false; } } async function supportsFetch() { if (supports === null) { supports = checkFetch(); } return await supports; } // We always polyfill fetch because Node's fetch doesn't support file URLs. globalThis.fetch = async function (resource, options) { const request = new Request(resource, options); const url = new URL(request.url); if (!(await supportsFetch()) && url.protocol === "file:") { const readStream = $fs.createReadStream(url); const headers = {}; const type = $mime.getType(url.pathname); if (type) { headers["Content-Type"] = type; } return new Response(readStream, { status: 200, statusText: "OK", headers, }); } else { return await oldFetch(request); } }; // @ts-ignore if (globalThis.XMLHttpRequest == null) { globalThis.XMLHttpRequest = class extends $xmlhttprequest.XMLHttpRequest { // We have to override the methods inside of the `constructor` // because `xmlhttprequest-ssl` doesn't use a regular class, // instead it defines all of the methods inside of the constructor. constructor(...args) { super(...args); const open = this.open; const send = this.send; let _async = true; let _url = null; let _mime = "text/xml"; function reset() { _async = true; _url = null; _mime = "text/xml"; } this.open = function (method, url, async, user, password) { // Special behavior for synchronous requests if (method === "GET" && !async && !user && !password) { _async = false; _url = url; // Default to the normal polyfill for async requests } else { reset(); return open.call(this, method, url, async, user, password); } }; this.send = function (data) { if (_async) { return send.call(this, data); // Use `sync-request` for synchronous requests. } else { const response = $request("GET", _url, { headers: { "Content-Type": _mime, } }); const buffer = response.body.buffer; const responseText = new TextDecoder("iso-8859-5", { fatal: true }).decode(buffer); this.status = 200; this.response = this.responseText = responseText; reset(); } }; this.overrideMimeType = function (mime) { _mime = mime; }; } }; } // This is technically not a part of the Worker polyfill, // but Workers are used for multi-threading, so this is often // needed when writing Worker code. if (globalThis.navigator == null) { globalThis.navigator = { hardwareConcurrency: $os.cpus().length, }; } if (globalThis.Worker == null) { globalThis.Worker = class Worker extends EventTarget { _worker; constructor(url, options) { super(); if (url instanceof URL) { if (url.protocol !== "file:") { throw new Error("Worker only supports file: URLs"); } url = url.href; } else { throw new Error("Filepaths are unreliable, use `new URL(\"...\", import.meta.url)` instead."); } if (!options || options.type !== "module") { throw new Error("Workers must use \`type: \"module\"\`"); } const code = ` import("node:worker_threads") .then(({ workerData }) => { return import(workerData.polyfill) .then(() => import(workerData.url)) }) .catch((e) => { // TODO maybe it should send a message to the parent? console.error(e.stack); }); `; this._worker = new $worker.Worker(code, { eval: true, workerData: { url, polyfill: new URL("node-polyfill.js", import.meta.url).href, }, }); this._worker.on("message", (data) => { this.dispatchEvent(new MessageEvent("message", { data })); }); this._worker.on("messageerror", (error) => { throw new Error("UNIMPLEMENTED"); }); this._worker.on("error", (error) => { // TODO attach the error to the event somehow const event = new Event("error"); this.dispatchEvent(event); }); } set onmessage(f) { throw new Error("UNIMPLEMENTED"); } set onmessageerror(f) { throw new Error("UNIMPLEMENTED"); } set onerror(f) { throw new Error("UNIMPLEMENTED"); } postMessage(value, transfer) { this._worker.postMessage(value, transfer); } terminate() { this._worker.terminate(); } // This is Node-specific, it allows the process to exit // even if the Worker is still running. unref() { this._worker.unref(); } }; } if (!$worker.isMainThread) { const globals = globalThis; // This is used to create the onmessage, onmessageerror, and onerror setters const makeSetter = (prop, event) => { let oldvalue; Object.defineProperty(globals, prop, { get() { return oldvalue; }, set(value) { if (oldvalue) { globals.removeEventListener(event, oldvalue); } oldvalue = value; if (oldvalue) { globals.addEventListener(event, oldvalue); } }, }); }; // This makes sure that `f` is only run once const memoize = (f) => { let run = false; return () => { if (!run) { run = true; f(); } }; }; // We only start listening for messages / errors when the worker calls addEventListener const startOnMessage = memoize(() => { $worker.parentPort.on("message", (data) => { workerEvents.dispatchEvent(new MessageEvent("message", { data })); }); }); const startOnMessageError = memoize(() => { throw new Error("UNIMPLEMENTED"); }); const startOnError = memoize(() => { $worker.parentPort.on("error", (data) => { workerEvents.dispatchEvent(new Event("error")); }); }); // Node workers don't have top-level events, so we have to make our own const workerEvents = new EventTarget(); globals.close = () => { process.exit(); }; globals.addEventListener = (type, callback, options) => { workerEvents.addEventListener(type, callback, options); if (type === "message") { startOnMessage(); } else if (type === "messageerror") { startOnMessageError(); } else if (type === "error") { startOnError(); } }; globals.removeEventListener = (type, callback, options) => { workerEvents.removeEventListener(type, callback, options); }; function postMessage(value, transfer) { $worker.parentPort.postMessage(value, transfer); } globals.postMessage = postMessage; makeSetter("onmessage", "message"); makeSetter("onmessageerror", "messageerror"); makeSetter("onerror", "error"); } if (!globalThis.self) { globalThis.self = globalThis; } //# sourceMappingURL=node-polyfill.js.map