UNPKG

@juit/lib-fetch-mock

Version:
186 lines (185 loc) 5.58 kB
// index.ts import assert from "node:assert"; import { STATUS_CODES } from "node:http"; var mockSymbol = /* @__PURE__ */ Symbol.for("juit.fetch.mock"); process.on("exit", () => process.exit(process.exitCode)); var FetchMockImpl = class { _handlers = []; _fetch; _baseurl; constructor(baseurl) { this._baseurl = new URL(baseurl || "http://test/"); } /* === FETCH ============================================================== */ async $fetch(info, init) { assert(this._fetch, "Global `fetch` not available (mock not enabled?)"); const realFetch = this._fetch; const fetchWrapper = (info2, init2) => realFetch.call(globalThis, info2, init2); let response = void 0; for (const handler of this._handlers) { const request = info instanceof URL || typeof info === "string" ? new Request(new URL(info, this._baseurl), init) : new Request(info.url, info); const result = await handler(request, fetchWrapper); if (result === void 0 || result === null) continue; response = typeof result === "number" ? sendStatus(result) : result; break; } if (response) return response; if (this._fetch[mockSymbol]) { return this._fetch[mockSymbol].$fetch(info, init); } return sendStatus(404); } /* === HANDLERS =========================================================== */ on(method, path, handler) { this._handlers.push((request, fetch) => { if (request.method !== method.toUpperCase()) return; const pathname = new URL(request.url).pathname; if (typeof path === "string" && path === pathname || path instanceof RegExp && pathname.match(path)) { return handler(request, fetch); } }); return this; } handle(handler) { this._handlers.push(handler); return this; } reset() { this._handlers = []; return this; } /* === INTERCEPTOR ======================================================== */ intercept() { const queue = []; queue.push(new Deferred()); const next = () => { return new Promise((resolve, reject) => { queue[0].promise.then((request) => { queue.splice(0, 1); resolve(request); }, reject); }); }; this._handlers.push((request, fetch) => { queue.push(new Deferred()); const deferredResponse = new Deferred(); const deferredRequest = new DeferredRequestImpl(request, deferredResponse, fetch); queue[queue.length - 2].resolve(deferredRequest); return deferredResponse.promise; }); return next; } /* === INSTALL / DESTROY ================================================== */ install() { if (mockSymbol in globalThis.fetch) { if (globalThis.fetch[mockSymbol] === this) return this; } let fn = globalThis.fetch; while (fn && fn[mockSymbol]) { if (fn[mockSymbol] === this) { throw new Error("Global `fetch` already mocked by this instance"); } else { fn = fn[mockSymbol]._fetch; } } this._fetch = globalThis.fetch; globalThis.fetch = this.$fetch.bind(this); Object.defineProperty(globalThis.fetch, mockSymbol, { value: this }); return this; } destroy() { if (!(mockSymbol in globalThis.fetch)) return; if (!this._fetch) return; let parent = void 0; let fn = globalThis.fetch; while (fn[mockSymbol]) { if (fn[mockSymbol] === this) { if (!parent) globalThis.fetch = this._fetch; else parent._fetch = this._fetch; break; } else { parent = fn[mockSymbol]; fn = fn[mockSymbol]._fetch; } } this._fetch = void 0; } }; var FetchMock = FetchMockImpl; var Deferred = class { promise; resolve; reject; constructor() { let resolve; let reject; this.promise = new Promise((resolver, rejector) => { resolve = resolver; reject = rejector; }); this.resolve = resolve; this.reject = reject; } }; var DeferredRequestImpl = class extends Request { constructor(request, _deferred, _fetch) { super(request); this._deferred = _deferred; this._fetch = _fetch; } _deferred; _fetch; fail(failure) { this._deferred.reject(failure || new Error(`Error: ${this.url}`)); } fetch(...args) { this._deferred.resolve(this._fetch(...args)); } send(response) { this._deferred.resolve(response || new Response()); } sendStatus(status) { this._deferred.resolve(sendStatus(status)); } sendText(text, status = 200) { this._deferred.resolve(sendText(text, status)); } sendJson(json, status = 200) { this._deferred.resolve(sendJson(json, status)); } sendData(data, status = 200) { this._deferred.resolve(sendData(data, status)); } }; function sendStatus(status, statusText = STATUS_CODES[status]) { return new Response(void 0, { status, statusText }); } function sendText(text, status = 200) { return new Response(text, { headers: { "content-type": "text/plain; charset=utf-8" }, statusText: STATUS_CODES[status], status }); } function sendJson(data, status = 200) { return new Response(JSON.stringify(data), { headers: { "content-type": "application/json; charset=utf-8" }, statusText: STATUS_CODES[status], status }); } function sendData(data, status = 200) { return new Response(Buffer.from(data), { headers: { "content-type": "application/octet-stream" }, statusText: STATUS_CODES[status], status }); } export { FetchMock, sendData, sendJson, sendStatus, sendText }; //# sourceMappingURL=index.mjs.map