🎉 Initial commit

This commit is contained in:
2026-02-02 08:45:51 +00:00
commit 90b460b899
8 changed files with 5100 additions and 0 deletions
Vendored
+24
View File
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+4393
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
{
"name": "remote-data-fp-ts",
"private": true,
"version": "0.0.1",
"type": "module",
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./remote-data-fp-ts": {
"types": "./dist/remote-data-fp-ts.d.ts",
"import": "./dist/remote-data-fp-ts.js"
}
},
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "tsc && vite build && tsc --emitDeclarationOnly --declaration --noEmit false --outDir dist/es && tsc --emitDeclarationOnly --declaration --noEmit false --outDir dist/cjs --module CommonJS --moduleResolution Node",
"build2": "tsup ./src/index.ts ./src/remote-data-fp-ts.ts --dts --format esm,cjs",
"build3-vite-rollup-plugin": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.15.3",
"@rollup/plugin-typescript": "11.1.6",
"fp-ts": "^2.16.8",
"publint": "0.2.9",
"tslib": "2.6.3",
"tsup": "^8.2.3",
"typescript": "^5.5.4",
"vite": "^5.3.4",
"vite-plugin-checker": "0.7.2",
"vite-plugin-dts": "4.0.0-beta.1",
"vite-plugin-externalize-deps": "^0.8.0"
},
"peerDependencies": {
"fp-ts": "^2.16.8"
}
}
+2
View File
@@ -0,0 +1,2 @@
import * as remoteData from "./remote-data-fp-ts.ts";
export { remoteData };
+578
View File
@@ -0,0 +1,578 @@
import * as chainable from "fp-ts/Chain";
import { Either, foldW as foldWEither } from "fp-ts/Either";
import { LazyArg, identity, pipe } from "fp-ts/function";
import { Alt3 } from "fp-ts/Alt";
import { Applicative3 } from "fp-ts/Applicative";
import { Apply3 } from "fp-ts/Apply";
import { Bifunctor3 } from "fp-ts/Bifunctor";
import { FromEither3 } from "fp-ts/FromEither";
import { Functor3 } from "fp-ts/Functor";
import { Monad3 } from "fp-ts/Monad";
import { Pointed3 } from "fp-ts/Pointed";
import { Show } from "fp-ts/Show";
// const makeADTUsingType = makeADT("_tag");
// export const makeRemoteResultADT = <
// TLoaded extends Record<string, unknown> | {} = {},
// TError extends Record<string, unknown> | {} = {},
// TLoading extends Record<string, unknown> | {} = {}
// >() => makeADTUsingType<RemoteResult<TLoaded, TError, TLoading>>({
// NotLoaded: ofType<RemoteResultNotLoaded>(),
// Loading: ofType<RemoteResultLoading<TLoading>>(),
// Failure: ofType<RemoteResultFailure<TError>>(),
// Loaded: ofType<RemoteResultLoaded<TLoaded>>(),
// });
export type RemoteResultNotLoaded = { readonly _tag: "NotLoaded"; };
export type RemoteResultLoading<L> = { readonly _tag: "Loading"; value: L; };
export type RemoteResultFailure<F> = { readonly _tag: "Failure"; error: F; };
export type RemoteResultLoaded<A> = { readonly _tag: "Loaded"; value: A; };
// ============================================================================
// Model
// ============================================================================
/**
* @category model
*/
export type RemoteResult<L, F, A> =
| RemoteResultFailure<F>
| RemoteResultLoading<L>
| RemoteResultLoaded<A>
| RemoteResultNotLoaded;
// ============================================================================
// Constructors
// ============================================================================
/**
* @category constructors
*/
export const notLoadedS: RemoteResultNotLoaded = { _tag: "NotLoaded" };
/**
* @category constructors
*/
export const loadingS = <L = never>(value: L): RemoteResultLoading<L> => ({
_tag: "Loading",
value
});
/**
* @category constructors
*/
export const failureS = <F = never>(error: F): RemoteResultFailure<F> => ({
_tag: "Failure",
error
});
/**
* @category constructors
*/
export const loadedS = <A = never>(value: A): RemoteResultLoaded<A> => ({
_tag: "Loaded",
value
});
/**
* @category constructors
*/
export const notLoaded: RemoteResult<never, never, never> = notLoadedS;
/**
* @category constructors
*/
export const loading: <L = never, F = never, A = never>(value: L) => RemoteResult<L, F, A> = loadingS;
/**
* @category constructors
*/
export const failure: <L = never, F = never, A = never>(error: F) => RemoteResult<L, F, A> = failureS;
/**
* @category constructors
*/
export const loaded: <L = never, F = never, A = never>(value: A) => RemoteResult<L, F, A> = loadedS;
/**
* @category constructors
*/
export const of = loaded;
// ============================================================================
// Refinements
// ============================================================================
/**
* @category refinements
*/
export const isNotLoaded = <L, F, A>(
fa: RemoteResult<L, F, A>
): fa is RemoteResultNotLoaded => fa._tag === "NotLoaded";
/**
* @category refinements
*/
export const isLoading = <L, F, A>(
fa: RemoteResult<L, F, A>
): fa is RemoteResultLoading<L> => fa._tag === "Loading";
/**
* @category refinements
*/
export const isFailure = <L, F, A>(
fa: RemoteResult<L, F, A>
): fa is RemoteResultFailure<F> => fa._tag === "Failure";
/**
* @category refinements
*/
export const isLoaded = <L, F, A>(
fa: RemoteResult<L, F, A>
): fa is RemoteResultLoaded<A> => fa._tag === "Loaded";
// ============================================================================
// Lambdas
// ============================================================================
/**
* @category type lambdas
*/
export const URI = "RemoteResult";
/**
* @category type lambdas
*/
export type URI = typeof URI;
declare module "fp-ts/HKT" {
interface URItoKind3<R, E, A> {
readonly [URI]: RemoteResult<R, E, A>;
}
}
// ============================================================================
// Internal
// ============================================================================
const _map: Functor3<URI>["map"] = (fa, f) => pipe(fa, map(f));
const _ap: Apply3<URI>["ap"] = (fab, fa) => pipe(fab, ap(fa));
const _chain: chainable.Chain3<URI>["chain"] = (fab, fa) => pipe(fab, chain(fa));
const _bimap: Bifunctor3<URI>["bimap"] = (fa, f, g) => pipe(fa, bimap(f, g));
const _mapLeft: Bifunctor3<URI>["mapLeft"] = (fea, f) => pipe(fea, mapFailure(f));
const _alt: Alt3<URI>["alt"] = (fa, that) => pipe(fa, alt(that));
// ============================================================================
// Mapping
// ============================================================================
/**
* @category mapping
*/
export const map: <A, B>(f: (a: A) => B) => <F, L>(fa: RemoteResult<L, F, A>) => RemoteResult<L, F, B> =
f => fa => isLoaded(fa) ? loaded(f(fa.value)) : fa;
/**
* @category mapping
*/
export const mapFailure: <F1, F2>(f: (a: F1) => F2) => <L, A>(fa: RemoteResult<L, F1, A>) => RemoteResult<L, F2, A> =
f => fa => isFailure(fa) ? failure(f(fa.error)) : fa;
/**
* @category mapping
*/
export const mapLoading: <L1, L2>(f: (a: L1) => L2) => <F, A>(fa: RemoteResult<L1, F, A>) => RemoteResult<L2, F, A> =
f => fa => isLoading(fa) ? loading(f(fa.value)) : fa;
/**
* @category error handling
*/
export const bimap: <F1, F2, A, B>(
f: (e: F1) => F2,
g: (a: A) => B
) => <L>(fa: RemoteResult<L, F1, A>) => RemoteResult<L, F2, B> =
(f, g) => fa =>
isLoaded(fa) ? loaded(g(fa.value)) :
isFailure(fa) ? failure(f(fa.error)) :
fa;
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* @category error handling
*/
export const mapBoth: {
<F1, F2, A, B>(
f: (e: F1) => F2,
g: (a: A) => B
): <L>(
self: RemoteResult<L, F1, A>
) => RemoteResult<L, F2, B>;
<L, F1, A, F2, B>(
self: RemoteResult<L, F1, A>,
f: (e: F1) => F2,
g: (a: A) => B
): RemoteResult<L, F2, B>;
} = (...args: [any, any] | [any, any, any]) =>
args.length === 2 ?
bimap(...args) :
_bimap(...args) as any;
/* eslint-enable @typescript-eslint/no-explicit-any */
/**
* @category error handling
*/
export const alt: <L, F, A>(
that: () => RemoteResult<L, F, A>
) => (
fa: RemoteResult<L, F, A>
) => RemoteResult<L, F, A> = that => fa => isLoaded(fa) ? fa : that();
/**
* @category error handling
*/
export const getOrElseW: <L, F, B>(
onNotLoaded: (f?: F | L) => B
) => <A>(
ma: RemoteResult<L, F, A>
) => A | B = onNotLoaded => matchW(
onNotLoaded,
onNotLoaded,
onNotLoaded,
identity
);
/**
* @category error handling
*/
export const getOrElse: <L, F, A>(
onNotLoaded: (f?: F | L) => A
) => (
ma: RemoteResult<L, F, A>
) => A = getOrElseW;
// ============================================================================
// Apply
// ============================================================================
/**
*/
export const ap: <L, F, A>(
fa: RemoteResult<L, F, A>
) => <B>(
fab: RemoteResult<L, F, (a: A) => B>
) => RemoteResult<L, F, B> =
fa => fab =>
!isLoaded(fab) ? fab :
!isLoaded(fa) ? fa :
loaded(fab.value(fa.value));
// ============================================================================
// Sequencing
// ============================================================================
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* @category sequencing
*/
export const flatMap: {
<A, L2, F2, B>(f: (a: A) => RemoteResult<L2, F2, B>):
<L1, F1>(ma: RemoteResult<L1, F1, A>) => RemoteResult<L1 | L2, F1 | F2, B>;
<L1, F1, A, L2, F2, B>(ma: RemoteResult<L1, F1, A>, f: (a: A) => RemoteResult<L2, F2, B>):
RemoteResult<
L1 | L2,
F1 | F2,
B
>;
} = (...args: [any, any] | [any]) =>
args.length === 1 ?
chain(args[0]) :
_chain(...args) as any;
/* eslint-enable @typescript-eslint/no-explicit-any */
/**
* @category sequencing
*/
export const chainW: <F2, L2, A, B>(
f: (a: A) => RemoteResult<F2, L2, B>
) => <F1, L1>(
ma: RemoteResult<F1, L1, A>
) => RemoteResult<F1 | F2, L1 | L2, B> = f => ma => isLoaded(ma) ? f(ma.value) : ma;
/**
* @category sequencing
*/
export const chain: <L, F, A, B>(
f: (a: A) => RemoteResult<L, F, B>
) => (
ma: RemoteResult<L, F, A>
) => RemoteResult<L, F, B> = chainW;
/**
* @category sequencing
*/
export const flattenW: <L1, F1, L2, F2, A>(
mma: RemoteResult<L1, F1, RemoteResult<L2, F2, A>>
) => RemoteResult<L1 | L2, F1 | F2, A> = chainW(identity);
/**
* @category sequencing
*/
export const flatten: <L, F, A>(
mma: RemoteResult<L, F, RemoteResult<L, F, A>>
) => RemoteResult<L, F, A> = chain(identity);
// ============================================================================
// Pattern matching
// ============================================================================
/**
* @category pattern matching
*/
export const matchW: <L, F, A, B1, B2, B3, B4>(
onNotLoaded: LazyArg<B1>,
onLoading: (l: L) => B3,
onFailure: (f: F) => B2,
onLoaded: (a: A) => B4,
) => (
f: RemoteResult<L, F, A>
) => B1 | B2 | B3 | B4 = (
onNotLoaded,
onLoading,
onFailure,
onLoaded,
) => (
f,
) => {
switch (f._tag) {
case "NotLoaded": return onNotLoaded();
case "Failure": return onFailure(f.error);
case "Loading": return onLoading(f.value);
case "Loaded": return onLoaded(f.value);
}
};
/**
* Alias of [`match`](#match).
*
* @category pattern matching
*/
export const foldW = matchW;
/**
* @category pattern matching
*/
export const matchNW: <L, F, A, B1, B2, B3, B4>(pattern: {
NotLoaded: LazyArg<B1>,
Loading: (l: L) => B3,
Failure: (f: F) => B2,
Loaded: (a: A) => B4,
}) => (
f: RemoteResult<L, F, A>
) => B1 | B2 | B3 | B4 = ({
NotLoaded,
Loading,
Failure,
Loaded,
}) => foldW(NotLoaded, Loading, Failure, Loaded);
/**
* Alias of [`matchN`](#match).
*
* @category pattern matching
*/
export const foldNW = matchNW;
/**
* @category pattern matching
*/
export const match: <L, F, A, B>(
onNotLoaded: LazyArg<B>,
onLoading: (l: L) => B,
onFailure: (f: F) => B,
onLoaded: (a: A) => B,
) => (
f: RemoteResult<L, F, A>
) => B = matchW;
/**
* @category pattern matching
*/
export const fold = matchW;
/**
* @category pattern matching
*/
export const matchN: <L, F, A, B>(pattern: {
NotLoaded: LazyArg<B>,
Loading: (l: L) => B,
Failure: (f: F) => B,
Loaded: (a: A) => B,
}) => (
f: RemoteResult<L, F, A>
) => B = matchNW;
/**
* @category pattern matching
*/
export const foldN = matchNW;
// ============================================================================
// Conversions
// ============================================================================
/**
* @category conversions
*/
export const fromEither: <F, A, L = unknown>(
fa: Either<F, A>
) => RemoteResult<L, F, A> = foldWEither(failure, loaded);
// ============================================================================
// Instances
// ============================================================================
/**
* @category instances
*/
export const Functor: Functor3<URI> = {
URI,
map: _map
};
/**
* @category instances
*/
export const Apply: Apply3<URI> = {
URI,
map: _map,
ap: _ap
};
/**
* @category instances
*/
export const Applicative: Applicative3<URI> = {
URI,
map: _map,
ap: _ap,
of
};
/**
* @category instances
*/
export const Chain: chainable.Chain3<URI> = {
URI,
map: _map,
ap: _ap,
chain: _chain
};
/**
* @category instances
*/
export const Monad: Monad3<URI> = {
URI,
map: _map,
ap: _ap,
of,
chain: _chain
};
/**
* @category instances
*/
export const Pointed: Pointed3<URI> = {
URI,
of
};
/**
* @category instances
*/
export const Bifunctor: Bifunctor3<URI> = {
URI,
bimap: _bimap,
mapLeft: _mapLeft,
};
/**
* @category instances
*/
export const Alt: Alt3<URI> = {
URI,
map: _map,
alt: _alt
};
/**
* @category instances
*/
export const FromEither: FromEither3<URI> = {
URI,
fromEither
};
/**
* @category instances
*/
export const getShow = <L, F, A>(
showLoading: Show<L>,
showFailure: Show<F>,
showLoaded: Show<A>
): Show<RemoteResult<L, F, A>> => ({
show: fold(
() => `remoteResultNotLoaded`,
l => `remoteResultFailure(${showLoading.show(l)})`,
f => `remoteResultFailure(${showFailure.show(f)})`,
l => `remoteResultFailure(${showLoaded.show(l)})`,
)
});
// ============================================================================
// Combinator
// ============================================================================
const _chainFirst: <R1, E1, A, R2, E2, _>(
ma: RemoteResult<R1, E1, A>,
f: (a: A) => RemoteResult<R2, E2, _>
) => RemoteResult<
R1 | R2,
E1 | E2,
A
> = (ma, f) => pipe(ma, chainFirstW(f));
/**
* Alias of `tap`.
*
* @category combinators
*/
export const chainFirst: <L, F, A, B>(
f: (a: A) => RemoteResult<L, F, B>
) => (ma: RemoteResult<L, F, A>) => RemoteResult<L, F, A> = chainable.chainFirst(Chain);
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Alias of `tap`.
*
* @category combinators
*/
export const chainFirstW: <L2, F2, A, B>(
f: (a: A) => RemoteResult<L2, F2, B>
) => <L1, F1>(ma: RemoteResult<L1, F1, A>) => RemoteResult<L1 | L2, F1 | F2, A> = chainFirst as any;
/**
* @category combinators
*/
export const tap: {
<R1, E1, A, R2, E2, _>(
self: RemoteResult<R1, E1, A>,
f: (a: A) => RemoteResult<R2, E2, _>
): RemoteResult<
R1 & R2,
E1 | E2,
A
>;
<A, R2, E2, _>(
f: (a: A) => RemoteResult<R2, E2, _>
): <R1, E1>(
self: RemoteResult<R1, E1, A>
) => RemoteResult<R1 & R2, E2 | E1, A>;
} = (...args: [any] | [any, any]) =>
args.length === 1 ?
chainFirst(...args) :
_chainFirst(...args) as any;
/* eslint-enable @typescript-eslint/no-explicit-any */
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"]
}
+33
View File
@@ -0,0 +1,33 @@
import checker from 'vite-plugin-checker';
import { defineConfig } from "vite";
import dts from 'vite-plugin-dts';
import { externalizeDeps } from "vite-plugin-externalize-deps";
import { resolve } from "path";
export default defineConfig({
plugins: [
externalizeDeps(),
checker({
typescript: true
}),
dts(),
],
build: {
minify: false,
lib: {
// Could also be a dictionary or array of multiple entry points
entry: resolve(__dirname, "src/index.ts"),
// name: "remote-data-fp-ts",
// the proper extensions will be added
fileName: (_, name) => `${name}.js`,
formats: ["es"]
},
emptyOutDir: true,
rollupOptions: {
output: {
preserveModules: true
}
},
},
});