@typed/fp
Version:
Data Structures and Resources for fp-ts
123 lines • 4.41 kB
JavaScript
/**
* @typed/fp/browser is a place to place implementations of environment from other modules that require or
* are best used with implementations specificall for the browser.
* @since 0.9.4
*/
import * as Ei from 'fp-ts/Either';
import * as D from './Disposable';
import * as E from './Env';
import * as R from './Resume';
import * as S from './Stream';
/**
* @category Environment
* @since 0.9.4
*/
export const HttpEnv = { http: E.fromResumeK(httpFetchRequest) };
function httpFetchRequest(uri, options = {}) {
return R.async((cb) => {
const { method = 'GET', headers = {}, body } = options;
const disposable = D.settable();
const abortController = new AbortController();
disposable.addDisposable({
dispose: () => abortController.abort(),
});
const init = {
method,
headers: Object.entries(headers).map(([key, value = '']) => [key, value]),
body,
credentials: 'include',
signal: abortController.signal,
};
async function makeRequest() {
const response = await fetch(uri, init);
const headers = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
const httpResponse = {
status: response.status,
body: await response.json().catch(() => response.text()),
headers,
};
if (!disposable.isDisposed()) {
disposable.addDisposable(cb(Ei.right(httpResponse)));
}
}
makeRequest().catch((error) => {
if (!disposable.isDisposed()) {
disposable.addDisposable(cb(Ei.left(error)));
}
});
return disposable;
});
}
/**
* Constructs an Adapter that utilizes a BroadcastChannel to communicate messages across
* all scripts of the same origin, including workers.
*
* _Note:_ An error will occur, and the stream will fail, if you send events which cannot be
* structurally cloned. See
* https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
* @category Constructor
* @since 0.12.2
*/
export const broadcastChannel = (name) => {
const channel = new BroadcastChannel(name);
const send = (event) => channel.postMessage(event);
const stream = S.newStream((sink, scheduler) => {
const onMessage = (event) => sink.event(scheduler.currentTime(), event.data);
const onMessageError = (event) => sink.error(scheduler.currentTime(), event.data);
channel.addEventListener('message', onMessage);
channel.addEventListener('messageerror', onMessageError);
return {
dispose: () => {
channel.removeEventListener('message', onMessage);
channel.removeEventListener('messageerror', onMessageError);
},
};
});
return [send, stream];
};
/**
* Utilize the Crypto API to generate 8-bit numbers
* @category Constructor
* @since 0.12.2
*/
export const random8Bits = (count) => E.fromIO(() => [...crypto.getRandomValues(new Uint8Array(count))]);
/**
* Utilize the Crypto API to generate 16-bit numbers
* @category Constructor
* @since 0.12.2
*/
export const random16Bits = (count) => E.fromIO(() => [...crypto.getRandomValues(new Uint16Array(count))]);
/**
* Utilize the Crypto API to generate 32-bit numbers
* @category Constructor
* @since 0.12.2
*/
export const random32Bits = (count) => E.fromIO(() => [...crypto.getRandomValues(new Uint32Array(count))]);
/**
* @category Environment
* @since 0.13.2
*/
export const rafEnv = {
raf: R.async((resume) => {
const disposable = D.settable();
let handle = requestAnimationFrame(() => (handle = requestAnimationFrame((n) => disposable.addDisposable(resume(n)))));
disposable.addDisposable({ dispose: () => cancelAnimationFrame(handle) });
return disposable;
}),
};
/**
* @category Environment
* @since 0.13.2
*/
export const whenIdleEnv = {
whenIdle: R.async((cb) => {
const disposable = D.settable();
const handle = window.requestIdleCallback((d) => disposable.addDisposable(cb(d)));
disposable.addDisposable({ dispose: () => cancelAnimationFrame(handle) });
return disposable;
}),
};
//# sourceMappingURL=browser.js.map