From 9ffc57e63e4f81402e23d584d73ce4fd7bea2a99 Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Fri, 26 Aug 2022 09:59:57 -0700 Subject: [PATCH] feat: expose ability to override fetch implementation (#6) --- README.md | 20 +++ .../micro-frame/component/node.marko | 118 ++++++++---------- .../micro-frame/component/web.component.ts | 13 +- 3 files changed, 86 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index 494e28c..0df5b3a 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,26 @@ and fallback to the network otherwise ``` +## `fetch` + +Optionally provide function to override default `fetch` logic. + +```marko + +``` + ## `timeout` A timeout in `ms` (defaults to 30s) that will prematurely abort the request. This will trigger the `<@catch>` if provided. diff --git a/src/components/micro-frame/component/node.marko b/src/components/micro-frame/component/node.marko index 6d3149f..8bcae21 100644 --- a/src/components/micro-frame/component/node.marko +++ b/src/components/micro-frame/component/node.marko @@ -2,22 +2,9 @@ import path from "path"; import fetch from "make-fetch-happen"; static const cachePath = path.resolve("node_modules/.cache/fetch"); -static function createDeferredPromise() { - let resolve; - let reject; - const promise = new Promise((_resolve, _reject) => { - resolve = _resolve; - reject = _reject; - }); - promise.resolve = resolve; - promise.reject = reject; - return promise; -} +static const strictSSL = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0"; -$ let buf = ""; -$ let hasMore = true; -$ let sentData = false; -$ let next = (async () => { +static async function fetchStream(input, out) { const incomingMessage = out.stream && (out.stream.req || out.stream.request); if (!incomingMessage) { @@ -25,36 +12,31 @@ $ let next = (async () => { } const url = new URL(input.src, `${incomingMessage.protocol}://${incomingMessage.headers.host}`); - const res = await fetch(url, { - cachePath, - cache: input.cache, - strictSSL: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0", - headers: { - ...incomingMessage.headers, - ...input.headers, - accept: "text/html", - } - }); + const { cache } = input; + const headers = { + ...incomingMessage.headers, + ...input.headers, + accept: "text/html", + }; + + const res = await (input.fetch + ? input.fetch(url, { cache, headers }, fetch) + : fetch(url, { + cache, + headers, + cachePath, + strictSSL, + }) + ); if (!res.ok) throw new Error(res.statusText); - next = createDeferredPromise(); - res.body - .on("data", data => { - buf += data.toString(); - if (next) next = next.resolve(); - }) - .on("error", err => { - if (next) next = next.reject(err); - else out.error(err); - }) - .on("end", () => { - hasMore = false; - if (next) next = next.resolve(); - }); + if (!res.body || !res.body[Symbol.asyncIterator]) { + throw new Error("Response body must be a stream."); + } - return next; -})(); + return res.body[Symbol.asyncIterator](); +}
@@ -63,32 +45,42 @@ $ let next = (async () => { $!{``} - - - <@then> - $!{buf} - - $ buf = ""; - $ sentData = true; - $ next = createDeferredPromise(); - - - - - - $ out.bf("@_", component, true); - - $ out.ef(); + + <@then|iter|> + + + <@then|{ value, done }|> + + + + $ out.script(`((e,t,d)=>{t=document.getElementById(e);do{t.removeChild(d=t.firstChild)}while(d.data!==e)})(${JSON.stringify(component.id)});`); + + + + $!{value} + + + + <@catch|err|> + + + + <${input.catch}(err)/> + + + $ throw err; + + + + - - - $ out.script(`((e,t,d)=>{t=document.getElementById(e);do{t.removeChild(d=t.firstChild)}while(d.data!==e)})(${JSON.stringify(component.id)});`); - + + + + $ out.ef();
diff --git a/src/components/micro-frame/component/web.component.ts b/src/components/micro-frame/component/web.component.ts index d2ab717..8cd5a19 100644 --- a/src/components/micro-frame/component/web.component.ts +++ b/src/components/micro-frame/component/web.component.ts @@ -7,6 +7,11 @@ interface Input { loading?: unknown; cache?: RequestCache; headers?: Record; + fetch?: ( + url: string, + options: RequestInit, + fetch: typeof window.fetch + ) => Promise; } interface State { @@ -49,11 +54,15 @@ export = { let err: Error | undefined; try { - const res = await fetch(this.src, { + const options: RequestInit = { cache: this.input.cache, signal: controller.signal, headers: Object.assign({}, this.input.headers, { accept: "text/html" }), - }); + }; + + const res = await (this.input.fetch + ? this.input.fetch(this.src, options, fetch) + : fetch(this.src, options)); if (!res.ok) throw new Error(res.statusText); const reader = res.body!.getReader(); const decoder = new TextDecoder();