imp(+internal): removed custom Maybe monad in place of fp-ts implementation
DELETE
packages/core/src/+internal/fp/functor.ts
-export interface Functor<T> {
- map<R>(f: (value: T) => R): Functor<R>;
- flatMap<R>(f: (value: T) => Functor<R>): Functor<R>;
-}

MODIFY
packages/core/src/+internal/fp/index.ts
packages/core/src/+internal/fp/index.ts
-export * from './functor';
-export * from './maybe';
export * from './compose';

DELETE
packages/core/src/+internal/fp/maybe.ts
-import { Functor } from './functor';
-
-export type Nullable<T> = T | null | undefined;
-
-export const isNullable = value =>
- value === undefined || value === null;
-
-export interface MaybeCases<T, U> {
- some: (t: T) => U;
- none: () => U;
-}
-
-export class Maybe<T> implements Functor<T> {
- private constructor(private value: Nullable<T>) {}
-
- static some = <T>(value: T) => new Maybe(value);
-
- static none = <T>() => new Maybe<T>(null);
-
- static of = <T>(value: Nullable<T>): Maybe<T> =>
- !isNullable(value)
- ? Maybe.some(value as T)
- : Maybe.none<T>()
-
- map = <R>(f: (value: T) => R): Maybe<R> =>
- !isNullable(this.value)
- ? Maybe.some(f(this.value as T))
- : Maybe.none<R>()
-
- flatMap = <R>(f: (value: T) => Maybe<R>): Maybe<R> =>
- !isNullable(this.value)
- ? f(this.value as T)
- : Maybe.none<R>()
-
- caseOf = <U>(cases: MaybeCases<T, U>) =>
- !isNullable(this.value)
- ? cases.some(this.value as T)
- : cases.none()
-
- valueOr = (defaultValue: T): T =>
- !isNullable(this.value)
- ? this.value as T
- : defaultValue
-
- valueOrCompute = <R extends T>(f: () => R): T | R =>
- !isNullable(this.value)
- ? this.value as T | R
- : f()
-
- valueOrThrow(error?: Error): T {
- if (isNullable(this.value)) {
- throw (error || new Error('No value is available'));
- }
-
- return this.value as T;
- }
-}

DELETE
packages/core/src/+internal/fp/spec/maybe.spec.ts
-import { Maybe } from '../maybe';
-
-const increment = (v: number) => v + 1;
-
-describe('Maybe monad', () => {
-
- describe('#valueOr', () => {
- test('returns a defined value if Some', () => {
- const value = Maybe.of(2)
- .valueOr(0);
-
- expect(value).toEqual(2);
- });
-
- test('returns provided default if None', () => {
- const value = Maybe.of<number>(undefined)
- .valueOr(0);
-
- expect(value).toEqual(0);
- });
- });
-
- describe('#valueOrCompute', () => {
- test('returns a defined value if Some', () => {
- const value = Maybe.of(2)
- .valueOrCompute(() => 3);
-
- expect(value).toEqual(2);
- });
-
- test('returns provided default if None', () => {
- const value = Maybe.of<number>(undefined)
- .valueOrCompute(() => 3);
-
- expect(value).toEqual(3);
- });
- });
-
-
- describe('#valueOrThrow', () => {
- test('returns value if is Some', () => {
- const value = () => Maybe.of(2)
- .valueOrThrow();
-
- expect(() => value()).not.toThrowError();
- expect(value()).toEqual(2);
- });
-
- test('throws default error if None', () => {
- const value = () => Maybe.of<number>(undefined)
- .valueOrThrow();
-
- expect(() => value()).toThrowError(new Error('No value is available'));
- });
-
- test('throws defined error if None', () => {
- const value = () => Maybe.of<number>(undefined)
- .valueOrThrow(new Error('test'));
-
- expect(() => value()).toThrowError(new Error('test'));
- });
- });
-
- describe('#map', () => {
- test('maps value if Some', () => {
- const value = Maybe.of(2)
- .map(increment)
- .valueOr(0);
-
- expect(value).toEqual(3);
- });
-
- test('returns default if None', () => {
- const value = Maybe.of<number>(undefined)
- .map(increment)
- .valueOr(0);
-
- expect(value).toEqual(0);
- });
- });
-
- describe('#flatMap', () => {
- test('flattens Some monad and returns value', () => {
- const value = Maybe.of(2)
- .flatMap(v => Maybe.of('test'))
- .valueOr('');
-
- expect(value).toEqual('test');
- });
-
- test('flattens None monad and returns default', () => {
- const value = Maybe.of<number>(undefined)
- .flatMap(v => Maybe.of('test'))
- .valueOr('test');
-
- expect(value).toEqual('test');
- });
- });
-
- describe('#caseOf', () => {
- test('matches Some monad and returns value', () => {
- const value = Maybe.of(2)
- .caseOf({
- some: v => v + 1,
- none: () => 0,
- });
-
- expect(value).toEqual(3);
- });
-
- test('matches None monad and default value', () => {
- const value = Maybe.of<number>(undefined)
- .caseOf({
- some: v => v + 1,
- none: () => 0,
- });
-
- expect(value).toEqual(0);
- });
- });
-});

ADD
packages/core/src/+internal/utils/any.util.ts
+export const isNonNullable = <T>(value: T): value is NonNullable<T> =>
+ value !== undefined && value !== null;

MODIFY
packages/core/src/+internal/utils/array.util.ts
packages/core/src/+internal/utils/array.util.ts
-import { Maybe } from '../fp/maybe';
+import { fromNullable } from 'fp-ts/lib/Option';
export const getArrayFromEnum = (E: Object) =>
Object.keys(E).filter(key => typeof E[key as any] === 'number');
export const getHead = <T>(array: T[]) =>
- Maybe.of(array[0]);
+ fromNullable(array[0]);
export const getLast = <T>(array: T[]) =>
- Maybe.of(array[array.length - 1]);
+ fromNullable(array[array.length - 1]);
export const filterArray = <T>(f: (v: T) => boolean) => (array: T[]) =>
array.filter(f);

MODIFY
packages/core/src/+internal/utils/index.ts
packages/core/src/+internal/utils/index.ts
export * from './array.util';
export * from './string.util';
export * from './error.util';
+export * from './any.util';

MODIFY
packages/core/src/+internal/utils/spec/array.util.spec.ts
packages/core/src/+internal/utils/spec/array.util.spec.ts
@@ -24,8 +24,8 @@ describe('Array util', () => {
const filledArray = ['test'];
// when
- const none = getHead<string>(emptyArray).valueOr('');
- const some = getHead(filledArray).valueOr('');
+ const none = getHead<string>(emptyArray).getOrElse('');
+ const some = getHead(filledArray).getOrElse('');
// then
expect(none).toEqual('');
@@ -38,8 +38,8 @@ describe('Array util', () => {
const filledArray = ['test_1', 'test_2'];
// when
- const none = getLast<string>(emptyArray).valueOr('');
- const some = getLast(filledArray).valueOr('');
+ const none = getLast<string>(emptyArray).getOrElse('');
+ const some = getLast(filledArray).getOrElse('');
// then
expect(none).toEqual('');

MODIFY
packages/core/src/+internal/utils/string.util.ts
packages/core/src/+internal/utils/string.util.ts
-export function isString(data: any): data is string {
- return typeof data === 'string' || data instanceof String;
-}
+import { fromNullable } from 'fp-ts/lib/Option';
+
+export const isString = (value: any): value is string =>
+ typeof value === 'string' || value instanceof String;
export const trim = (strings: TemplateStringsArray, ...values: any[]) => {
const notNilValues = values.map(value =>
- value !== null && value !== undefined ? value : ''
+ fromNullable(value).getOrElse('')
);
-
const interpolation = strings.reduce(
(prev, current) => prev + current + (notNilValues.length ? notNilValues.shift() : ''), '',
);

MODIFY
packages/core/src/router/router.builder.ts
packages/core/src/router/router.builder.ts
import { Either, left, right } from 'fp-ts/lib/Either';
+import { isNonNullable } from '../+internal/utils';
import { HttpMiddlewareEffect, HttpEffect } from '../effects/http-effects.interface';
import { combineMiddlewares } from '../effects/effects.combiner';
import { coreErrorFactory } from '../error/error.factory';
@@ -26,17 +27,17 @@ const pipe = (
};
const matchPath = (path: string): Pipeable => route =>
- !!path
+ isNonNullable(path)
? route.map(r => ({ ...r, path }))
: left(coreErrorFactory('Route path has to be defined', { contextMethod: 'matchPath'}));
const matchType = (method: HttpMethod): Pipeable => route =>
- !!method
+ isNonNullable(method)
? route.map(r => ({ ...r, method }))
: left(coreErrorFactory('HttpMethod has to be defined', { contextMethod: 'matchType'}));
const use = (middleware: HttpMiddlewareEffect): Pipeable => route =>
- !!middleware
+ isNonNullable(middleware)
? route.map(r => ({
...r,
middleware: r.middleware
@@ -46,8 +47,8 @@ const use = (middleware: HttpMiddlewareEffect): Pipeable => route =>
: left(coreErrorFactory('Middleware has to be defined', { contextMethod: 'use'}));
const useEffect = (effect: HttpEffect): Pipeable => route =>
- !!effect
+ isNonNullable(effect)
? route.map(r => ({ ...r, effect }))
: left(coreErrorFactory('Effect has to be defined', { contextMethod: 'useEffect'}));
-export const r = { pipe, matchPath, matchType, useEffect, use };
+export const r = { pipe, matchPath, matchType, use, useEffect };

MODIFY
packages/core/src/router/router.query.factory.ts
packages/core/src/router/router.query.factory.ts
import * as querystring from 'querystring';
-import { Nullable, Maybe, getHead } from '../+internal';
+import { getHead } from '../+internal';
import { QueryParameters } from '../http.interface';
+import { fromNullable } from 'fp-ts/lib/Option';
const getNestedQueryParam = (params: Object) => (key: string): QueryParameters => {
const paramValue = params[key];
const nestedKeys = /\[.*]/.exec(key);
- return Maybe.of(nestedKeys)
- .flatMap(getHead)
+ return fromNullable(nestedKeys)
+ .chain(getHead)
.map(_ => _.replace(/\[/g, ''))
.map(_ => _.split(']'))
.map(_ => _.filter(v => v.length > 0))
@@ -15,12 +16,12 @@ const getNestedQueryParam = (params: Object) => (key: string): QueryParameters =
(value, key) => ({ [key]: value }),
paramValue,
))
- .flatMap(nestedQueryObject => Maybe.of(nestedKeys)
- .flatMap(getHead)
+ .chain(nestedQueryObject => fromNullable(nestedKeys)
+ .chain(getHead)
.map(head => key.replace(head, ''))
.map(key => ({ [key]: nestedQueryObject }))
)
- .valueOr({ [key]: paramValue });
+ .getOrElse({ [key]: paramValue });
};
const extractNestedQueryParams = (queryParams: Object): QueryParameters =>
@@ -29,8 +30,8 @@ const extractNestedQueryParams = (queryParams: Object): QueryParameters =>
...Object.keys(queryParams).map(getNestedQueryParam(queryParams))
);
-export const queryParamsFactory = (queryParams: Nullable<string>): QueryParameters =>
- Maybe.of(queryParams)
+export const queryParamsFactory = (queryParams: string | undefined | null): QueryParameters =>
+ fromNullable(queryParams)
.map(querystring.parse)
.map(extractNestedQueryParams)
- .valueOr({});
+ .getOrElse({});

MODIFY
packages/middleware-jwt/src/jwt.util.ts
packages/middleware-jwt/src/jwt.util.ts
+import { fromNullable } from 'fp-ts/lib/Option';
import { HttpRequest } from '@marblejs/core';
-import { Maybe } from '@marblejs/core/dist/+internal';
const splitHeader = (header: string) => header.split(' ');
const getLastElement = (array: string[]) => array[array.length - 1];
export const parseAuthorizationHeader = (req: HttpRequest) =>
- Maybe.of(req.headers.authorization)
+ fromNullable(req.headers.authorization)
.map(splitHeader)
.map(getLastElement)
- .valueOr('');
+ .getOrElse('');

MODIFY
packages/middleware-logger/src/logger.util.ts
packages/middleware-logger/src/logger.util.ts
import { WriteStream } from 'fs';
+import { fromNullable } from 'fp-ts/lib/Option';
import { HttpResponse } from '@marblejs/core';
-import { Maybe, compose } from '@marblejs/core/dist/+internal';
+import { compose } from '@marblejs/core/dist/+internal';
import { LoggerOptions } from './logger.model';
export const isNotSilent = (opts: LoggerOptions) => () =>
!opts.silent;
export const filterResponse = (opts: LoggerOptions) => (res: HttpResponse) =>
- Maybe.of(opts.filter)
+ fromNullable(opts.filter)
.map(filter => filter(res))
- .valueOr(true);
+ .getOrElse(true);
export const writeToStream = (stream: WriteStream, chunk: string) =>
stream.write(`${chunk}\n\n`);

imp(+internal): removed custom Maybe monad in place of fp-ts implementation