Add T prefix for all type defs

This commit is contained in:
Jerko Steiner 2019-04-02 16:22:38 +08:00
parent db96b95522
commit 264f5aba60
69 changed files with 240 additions and 242 deletions

View File

@ -1,6 +0,0 @@
import {IAction} from './IAction'
export type GetAction<MyTypes, T extends string> =
MyTypes extends IAction<infer U, T>
? MyTypes
: never

View File

@ -1,6 +0,0 @@
import {IAsyncAction} from './IAsyncAction'
export type GetPendingAction<MyTypes, T extends string> =
MyTypes extends IAsyncAction<infer U, T> & {status: 'pending'}
? MyTypes
: never

View File

@ -1,6 +0,0 @@
import {IAsyncAction} from './IAsyncAction'
export type GetResolvedAction<MyTypes, T extends string> =
MyTypes extends IAsyncAction<infer U, T> & {status: 'resolved'}
? MyTypes
: never

View File

@ -2,9 +2,9 @@ import {IPendingAction} from './IPendingAction'
import {IResolvedAction} from './IResolvedAction' import {IResolvedAction} from './IResolvedAction'
import {IRejectedAction} from './IRejectedAction' import {IRejectedAction} from './IRejectedAction'
export type IAsyncStatus = 'pending' | 'resolved' | 'rejected' export type TAsyncStatus = 'pending' | 'resolved' | 'rejected'
export type IAsyncAction<T, ActionType extends string> = export type TAsyncAction<T, ActionType extends string> =
IPendingAction<T, ActionType> IPendingAction<T, ActionType>
| IResolvedAction<T, ActionType> | IResolvedAction<T, ActionType>
| IRejectedAction<ActionType> | IRejectedAction<ActionType>

View File

@ -0,0 +1,6 @@
import {IAction} from './IAction'
export type TGetAction<ActionTypes, T extends string> =
ActionTypes extends IAction<infer U, T>
? ActionTypes
: never

View File

@ -0,0 +1,6 @@
import {TAsyncAction} from './TAsyncAction'
export type TGetPendingAction<MyTypes, T extends string> =
MyTypes extends TAsyncAction<infer U, T> & {status: 'pending'}
? MyTypes
: never

View File

@ -0,0 +1,6 @@
import {TAsyncAction} from './TAsyncAction'
export type TGetResolvedAction<MyTypes, T extends string> =
MyTypes extends TAsyncAction<infer U, T> & {status: 'resolved'}
? MyTypes
: never

View File

@ -1,9 +1,9 @@
export * from './GetAction'
export * from './GetResolvedAction'
export * from './GetPendingAction'
export * from './IAction' export * from './IAction'
export * from './IAsyncAction'
export * from './IPendingAction' export * from './IPendingAction'
export * from './IRejectedAction' export * from './IRejectedAction'
export * from './IResolvedAction' export * from './IResolvedAction'
export * from './PendingAction' export * from './PendingAction'
export * from './TAsyncAction'
export * from './TGetAction'
export * from './TGetPendingAction'
export * from './TGetResolvedAction'

View File

@ -1,9 +1,9 @@
import {createCRUDActions} from './CRUDActions' import {createCRUDActions} from './CRUDActions'
import React from 'react' import React from 'react'
import {AnyAction} from 'redux' import {AnyAction} from 'redux'
import {CRUDReducer, ICRUDMethod} from './' import {CRUDReducer, TCRUDMethod} from './'
import {HTTPClientMock, TestUtils, getError} from '../test-utils' import {HTTPClientMock, TestUtils, getError} from '../test-utils'
import {IMethod} from '@rondo/common' import {TMethod} from '@rondo/common'
import {IPendingAction} from '../actions' import {IPendingAction} from '../actions'
describe('CRUD', () => { describe('CRUD', () => {
@ -100,7 +100,7 @@ describe('CRUD', () => {
}) })
function dispatch( function dispatch(
method: ICRUDMethod, method: TCRUDMethod,
action: IPendingAction<unknown, string>, action: IPendingAction<unknown, string>,
) { ) {
store.dispatch(action) store.dispatch(action)
@ -109,13 +109,13 @@ describe('CRUD', () => {
return action return action
} }
function getUrl(method: ICRUDMethod) { function getUrl(method: TCRUDMethod) {
return method === 'save' || method === 'findMany' return method === 'save' || method === 'findMany'
? '/one/1/two' ? '/one/1/two'
: '/one/1/two/2' : '/one/1/two/2'
} }
function getHTTPMethod(method: ICRUDMethod): IMethod { function getHTTPMethod(method: TCRUDMethod): TMethod {
switch (method) { switch (method) {
case 'save': case 'save':
return 'post' return 'post'
@ -131,7 +131,7 @@ describe('CRUD', () => {
describe('Promise rejections', () => { describe('Promise rejections', () => {
const testCases: Array<{ const testCases: Array<{
method: ICRUDMethod method: TCRUDMethod
params: any params: any
}> = [{ }> = [{
method: 'findOne', method: 'findOne',
@ -203,7 +203,7 @@ describe('CRUD', () => {
const entity = {id: 100, name: 'test'} const entity = {id: 100, name: 'test'}
const testCases: Array<{ const testCases: Array<{
method: ICRUDMethod method: TCRUDMethod
params: any params: any
body?: any body?: any
response: any response: any

View File

@ -1,12 +1,10 @@
import {ICRUDAction} from './ICRUDAction' import {TCRUDAction} from './TCRUDAction'
import {ICRUDMethod} from './ICRUDMethod' import {TCRUDMethod} from './TCRUDMethod'
import {IHTTPClient, ITypedRequestParams} from '../http' import {IHTTPClient, ITypedRequestParams} from '../http'
import {IRoutes, Filter, OnlyDefined} from '@rondo/common' import {IRoutes, TFilter, TOnlyDefined} from '@rondo/common'
export type Optional<T> = T extends {} ? T : undefined type TAction<T, ActionType extends string, Method extends TCRUDMethod> =
TFilter<TCRUDAction<T, ActionType>, {method: Method, status: 'pending'}>
type Action<T, ActionType extends string, Method extends ICRUDMethod> =
Filter<ICRUDAction<T, ActionType>, {method: Method, status: 'pending'}>
export class SaveActionCreator< export class SaveActionCreator<
T extends IRoutes, T extends IRoutes,
@ -20,10 +18,10 @@ export class SaveActionCreator<
readonly type: ActionType, readonly type: ActionType,
) {} ) {}
save = (params: OnlyDefined<{ save = (params: TOnlyDefined<{
body: T[Route]['post']['body'], body: T[Route]['post']['body'],
params: T[Route]['post']['params'], params: T[Route]['post']['params'],
}>): Action<T[Route]['post']['response'], ActionType, 'save'> => { }>): TAction<T[Route]['post']['response'], ActionType, 'save'> => {
const p = params as any const p = params as any
return { return {
payload: this.http.post(this.route, p.body, p.params), payload: this.http.post(this.route, p.body, p.params),
@ -46,10 +44,10 @@ export class FindOneActionCreator<
readonly type: ActionType, readonly type: ActionType,
) {} ) {}
findOne = (params: OnlyDefined<{ findOne = (params: TOnlyDefined<{
query: Optional<T[Route]['get']['query']>, query: T[Route]['get']['query'],
params: T[Route]['get']['params'], params: T[Route]['get']['params'],
}>): Action<T[Route]['get']['response'], ActionType, 'findOne'> => { }>): TAction<T[Route]['get']['response'], ActionType, 'findOne'> => {
const p = params as any const p = params as any
return { return {
payload: this.http.get(this.route, p.query, p.params), payload: this.http.get(this.route, p.query, p.params),
@ -73,10 +71,10 @@ export class UpdateActionCreator<
readonly type: ActionType, readonly type: ActionType,
) {} ) {}
update = (params: OnlyDefined<{ update = (params: TOnlyDefined<{
body: T[Route]['put']['body'], body: T[Route]['put']['body'],
params: T[Route]['put']['params'], params: T[Route]['put']['params'],
}>): Action<T[Route]['put']['response'], ActionType, 'update'> => { }>): TAction<T[Route]['put']['response'], ActionType, 'update'> => {
const p = params as any const p = params as any
return { return {
payload: this.http.put(this.route, p.body, p.params), payload: this.http.put(this.route, p.body, p.params),
@ -100,10 +98,10 @@ export class RemoveActionCreator<
readonly type: ActionType, readonly type: ActionType,
) {} ) {}
remove = (params: OnlyDefined<{ remove = (params: TOnlyDefined<{
body: Optional<T[Route]['delete']['body']>, body: T[Route]['delete']['body'],
params: T[Route]['delete']['params'], params: T[Route]['delete']['params'],
}>): Action<T[Route]['delete']['response'], ActionType, 'remove'> => { }>): TAction<T[Route]['delete']['response'], ActionType, 'remove'> => {
const p = params as any const p = params as any
return { return {
payload: this.http.delete(this.route, p.body, p.params), payload: this.http.delete(this.route, p.body, p.params),
@ -126,8 +124,8 @@ export class FindManyActionCreator<
readonly type: ActionType, readonly type: ActionType,
) {} ) {}
findMany = (params: OnlyDefined<{ findMany = (params: TOnlyDefined<{
query: Optional<T[Route]['get']['query']>, query: T[Route]['get']['query'],
params: T[Route]['get']['params'], params: T[Route]['get']['params'],
}>): { }>): {
payload: Promise<T[Route]['get']['response']> payload: Promise<T[Route]['get']['response']>

View File

@ -1,7 +1,7 @@
import {IAction, IResolvedAction} from '../actions' import {IAction, IResolvedAction} from '../actions'
import {ICRUDAction} from './ICRUDAction' import {TCRUDAction} from './TCRUDAction'
import {ICRUDMethod} from './ICRUDMethod' import {TCRUDMethod} from './TCRUDMethod'
import {indexBy, without, Filter} from '@rondo/common' import {indexBy, without, TFilter} from '@rondo/common'
export interface ICRUDEntity { export interface ICRUDEntity {
readonly id: number readonly id: number
@ -65,7 +65,7 @@ export class CRUDReducer<
handleRejected = ( handleRejected = (
state: ICRUDState<T>, state: ICRUDState<T>,
method: ICRUDMethod, method: TCRUDMethod,
error: Error, error: Error,
): ICRUDState<T> => { ): ICRUDState<T> => {
return { return {
@ -82,7 +82,7 @@ export class CRUDReducer<
handleLoading = ( handleLoading = (
state: ICRUDState<T>, state: ICRUDState<T>,
method: ICRUDMethod, method: TCRUDMethod,
): ICRUDState<T> => { ): ICRUDState<T> => {
return { return {
...state, ...state,
@ -163,7 +163,7 @@ export class CRUDReducer<
reduce = ( reduce = (
state: ICRUDState<T> | undefined, state: ICRUDState<T> | undefined,
action: ICRUDAction<T, ActionType>, action: TCRUDAction<T, ActionType>,
): ICRUDState<T> => { ): ICRUDState<T> => {
const {defaultState} = this const {defaultState} = this
state = state || defaultState state = state || defaultState

View File

@ -1,24 +0,0 @@
import {IAsyncAction} from '../actions'
import {ICRUDMethod} from './ICRUDMethod'
export type ICRUDSaveAction<T, ActionType extends string> =
IAsyncAction<T, ActionType> & {method: Extract<ICRUDMethod, 'save'>}
export type ICRUDUpdateAction<T, ActionType extends string> =
IAsyncAction<T, ActionType> & {method: Extract<ICRUDMethod, 'update'>}
export type ICRUDRemoveAction<T, ActionType extends string> =
IAsyncAction<T, ActionType> & {method: Extract<ICRUDMethod, 'remove'>}
export type ICRUDFindOneAction<T, ActionType extends string> =
IAsyncAction<T, ActionType> & {method: Extract<ICRUDMethod, 'findOne'>}
export type ICRUDFindManyAction<T, ActionType extends string> =
IAsyncAction<T[], ActionType> & {method: Extract<ICRUDMethod, 'findMany'>}
export type ICRUDAction<T, ActionType extends string> =
ICRUDSaveAction<T, ActionType>
| ICRUDUpdateAction<T, ActionType>
| ICRUDRemoveAction<T, ActionType>
| ICRUDFindOneAction<T, ActionType>
| ICRUDFindManyAction<T, ActionType>

View File

@ -1 +0,0 @@
export type ICRUDMethod = 'save' | 'update' | 'findOne' | 'findMany' | 'remove'

View File

@ -0,0 +1,24 @@
import {TAsyncAction} from '../actions'
import {TCRUDMethod} from './TCRUDMethod'
export type TCRUDSaveAction<T, ActionType extends string> =
TAsyncAction<T, ActionType> & {method: Extract<TCRUDMethod, 'save'>}
export type TCRUDUpdateAction<T, ActionType extends string> =
TAsyncAction<T, ActionType> & {method: Extract<TCRUDMethod, 'update'>}
export type TCRUDRemoveAction<T, ActionType extends string> =
TAsyncAction<T, ActionType> & {method: Extract<TCRUDMethod, 'remove'>}
export type TCRUDFindOneAction<T, ActionType extends string> =
TAsyncAction<T, ActionType> & {method: Extract<TCRUDMethod, 'findOne'>}
export type TCRUDFindManyAction<T, ActionType extends string> =
TAsyncAction<T[], ActionType> & {method: Extract<TCRUDMethod, 'findMany'>}
export type TCRUDAction<T, ActionType extends string> =
TCRUDSaveAction<T, ActionType>
| TCRUDUpdateAction<T, ActionType>
| TCRUDRemoveAction<T, ActionType>
| TCRUDFindOneAction<T, ActionType>
| TCRUDFindManyAction<T, ActionType>

View File

@ -0,0 +1 @@
export type TCRUDMethod = 'save' | 'update' | 'findOne' | 'findMany' | 'remove'

View File

@ -1,4 +1,4 @@
export * from './CRUDActions' export * from './CRUDActions'
export * from './CRUDReducer' export * from './CRUDReducer'
export * from './ICRUDAction' export * from './TCRUDAction'
export * from './ICRUDMethod' export * from './TCRUDMethod'

View File

@ -1,4 +1,4 @@
import {GetAction, IAction} from '../actions' import {TGetAction, IAction} from '../actions'
export interface ICrumbLink { export interface ICrumbLink {
name: string name: string
@ -10,10 +10,10 @@ export interface ICrumbs {
current: string current: string
} }
export type CrumbsActionType = export type TCrumbsAction =
IAction<ICrumbs, 'BREADCRUMBS_SET'> IAction<ICrumbs, 'BREADCRUMBS_SET'>
type Action<T extends string> = GetAction<CrumbsActionType, T> type Action<T extends string> = TGetAction<TCrumbsAction, T>
export class CrumbsActions { export class CrumbsActions {
setCrumbs(breadcrumbs: ICrumbs): Action<'BREADCRUMBS_SET'> { setCrumbs(breadcrumbs: ICrumbs): Action<'BREADCRUMBS_SET'> {

View File

@ -1,12 +1,12 @@
import {Crumb} from './Crumb' import {Crumb} from './Crumb'
import {Connector, IStateSelector} from '../redux' import {Connector, TStateSelector} from '../redux'
import {ICrumbsState} from './CrumbsReducer' import {ICrumbsState} from './CrumbsReducer'
import {CrumbsActions} from './CrumbsActions' import {CrumbsActions} from './CrumbsActions'
export class CrumbsConnector extends Connector<ICrumbsState> { export class CrumbsConnector extends Connector<ICrumbsState> {
protected readonly breadcrumbsActions = new CrumbsActions() protected readonly breadcrumbsActions = new CrumbsActions()
connect<State>(getLocalState: IStateSelector<State, ICrumbsState>) { connect<State>(getLocalState: TStateSelector<State, ICrumbsState>) {
return this.wrap( return this.wrap(
getLocalState, getLocalState,
state => state, state => state,

View File

@ -1,4 +1,4 @@
import {ICrumbs, CrumbsActionType} from './CrumbsActions' import {ICrumbs, TCrumbsAction} from './CrumbsActions'
export interface ICrumbsState extends ICrumbs { export interface ICrumbsState extends ICrumbs {
} }
@ -8,7 +8,7 @@ const defaultState: ICrumbsState = {
current: 'Home', current: 'Home',
} }
export function Crumbs(state = defaultState, action: CrumbsActionType) export function Crumbs(state = defaultState, action: TCrumbsAction)
: ICrumbsState { : ICrumbsState {
switch (action.type) { switch (action.type) {
case 'BREADCRUMBS_SET': case 'BREADCRUMBS_SET':

View File

@ -1,7 +1,7 @@
import axios from 'axios' import axios from 'axios'
import {IHTTPClient} from './IHTTPClient' import {IHTTPClient} from './IHTTPClient'
import {IHeader} from './IHeader' import {IHeader} from './IHeader'
import {IMethod, IRoutes, URLFormatter} from '@rondo/common' import {TMethod, IRoutes, URLFormatter} from '@rondo/common'
import {IRequest} from './IRequest' import {IRequest} from './IRequest'
import {IResponse} from './IResponse' import {IResponse} from './IResponse'
import {ITypedRequestParams} from './ITypedRequestParams' import {ITypedRequestParams} from './ITypedRequestParams'
@ -31,7 +31,7 @@ export class HTTPClient<T extends IRoutes> implements IHTTPClient<T> {
async request< async request<
P extends keyof T & string, P extends keyof T & string,
M extends IMethod, M extends TMethod,
>(params: ITypedRequestParams<T, P, M>): Promise<T[P][M]['response']> { >(params: ITypedRequestParams<T, P, M>): Promise<T[P][M]['response']> {
const url = this.formatter.format(params.path, params.params) const url = this.formatter.format(params.path, params.params)

View File

@ -1,10 +1,10 @@
import {IMethod, IRoutes} from '@rondo/common' import {TMethod, IRoutes} from '@rondo/common'
import {ITypedRequestParams} from './ITypedRequestParams' import {ITypedRequestParams} from './ITypedRequestParams'
export interface IHTTPClient<T extends IRoutes> { export interface IHTTPClient<T extends IRoutes> {
request< request<
P extends keyof T & string, P extends keyof T & string,
M extends IMethod, M extends TMethod,
>(params: ITypedRequestParams<T, P, M>): Promise<T[P][M]['response']> >(params: ITypedRequestParams<T, P, M>): Promise<T[P][M]['response']>
get<P extends keyof T & string>( get<P extends keyof T & string>(

View File

@ -1,7 +1,7 @@
import {IMethod} from '@rondo/common' import {TMethod} from '@rondo/common'
export interface IRequest { export interface IRequest {
method: IMethod, method: TMethod,
url: string, url: string,
params?: {[key: string]: any}, params?: {[key: string]: any},
data?: any, data?: any,

View File

@ -1,9 +1,9 @@
import {IRoutes, IMethod} from '@rondo/common' import {IRoutes, TMethod} from '@rondo/common'
export interface ITypedRequestParams< export interface ITypedRequestParams<
T extends IRoutes, T extends IRoutes,
P extends keyof T & string, P extends keyof T & string,
M extends IMethod, M extends TMethod,
> { > {
method: M, method: M,
path: P, path: P,

View File

@ -1,14 +1,14 @@
import {GetAction, IAsyncAction, IAction, PendingAction} from '../actions' import {TGetAction, TAsyncAction, IAction, PendingAction} from '../actions'
import {IAPIDef, ICredentials, INewUser, IUser} from '@rondo/common' import {IAPIDef, ICredentials, INewUser, IUser} from '@rondo/common'
import {IHTTPClient} from '../http/IHTTPClient' import {IHTTPClient} from '../http/IHTTPClient'
export type LoginActionType = export type TLoginAction =
IAsyncAction<IUser, 'LOGIN'> TAsyncAction<IUser, 'LOGIN'>
| IAsyncAction<unknown, 'LOGIN_LOGOUT'> | TAsyncAction<unknown, 'LOGIN_LOGOUT'>
| IAsyncAction<IUser, 'LOGIN_REGISTER'> | TAsyncAction<IUser, 'LOGIN_REGISTER'>
| IAction<{redirectTo: string}, 'LOGIN_REDIRECT_SET'> | IAction<{redirectTo: string}, 'LOGIN_REDIRECT_SET'>
type Action<T extends string> = GetAction<LoginActionType, T> type TAction<T extends string> = TGetAction<TLoginAction, T>
export class LoginActions { export class LoginActions {
constructor(protected readonly http: IHTTPClient<IAPIDef>) {} constructor(protected readonly http: IHTTPClient<IAPIDef>) {}
@ -34,7 +34,7 @@ export class LoginActions {
) )
} }
setRedirectTo = (redirectTo: string): Action<'LOGIN_REDIRECT_SET'> => { setRedirectTo = (redirectTo: string): TAction<'LOGIN_REDIRECT_SET'> => {
return { return {
payload: {redirectTo}, payload: {redirectTo},
type: 'LOGIN_REDIRECT_SET', type: 'LOGIN_REDIRECT_SET',

View File

@ -1,7 +1,7 @@
import {Connector} from '../redux/Connector' import {Connector} from '../redux/Connector'
import {ICredentials} from '@rondo/common' import {ICredentials} from '@rondo/common'
import {ILoginState} from './LoginReducer' import {ILoginState} from './LoginReducer'
import {IStateSelector} from '../redux' import {TStateSelector} from '../redux'
import {LoginActions} from './LoginActions' import {LoginActions} from './LoginActions'
import {LoginForm} from './LoginForm' import {LoginForm} from './LoginForm'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
@ -18,7 +18,7 @@ export class LoginConnector extends Connector<ILoginState> {
super() super()
} }
connect<State>(getLocalState: IStateSelector<State, ILoginState>) { connect<State>(getLocalState: TStateSelector<State, ILoginState>) {
return this.wrap( return this.wrap(
getLocalState, getLocalState,
state => ({ state => ({

View File

@ -1,5 +1,5 @@
import {IUser} from '@rondo/common' import {IUser} from '@rondo/common'
import {LoginActionType} from './LoginActions' import {TLoginAction} from './LoginActions'
export interface ILoginState { export interface ILoginState {
readonly error: string readonly error: string
@ -17,7 +17,7 @@ const defaultState: ILoginState = {
export function Login( export function Login(
state = defaultState, state = defaultState,
action: LoginActionType, action: TLoginAction,
): ILoginState { ): ILoginState {
switch (action.type) { switch (action.type) {
// sync actions // sync actions

View File

@ -1,7 +1,7 @@
import {Connector} from '../redux/Connector' import {Connector} from '../redux/Connector'
import {INewUser} from '@rondo/common' import {INewUser} from '@rondo/common'
import {ILoginState} from './LoginReducer' import {ILoginState} from './LoginReducer'
import {IStateSelector} from '../redux' import {TStateSelector} from '../redux'
import {LoginActions} from './LoginActions' import {LoginActions} from './LoginActions'
import {RegisterForm} from './RegisterForm' import {RegisterForm} from './RegisterForm'
import {bindActionCreators} from 'redux' import {bindActionCreators} from 'redux'
@ -20,7 +20,7 @@ export class RegisterConnector extends Connector<ILoginState> {
super() super()
} }
connect<State>(getLocalState: IStateSelector<State, ILoginState>) { connect<State>(getLocalState: TStateSelector<State, ILoginState>) {
return this.wrap( return this.wrap(
getLocalState, getLocalState,
state => ({ state => ({

View File

@ -1,4 +1,4 @@
import {IStateSelector} from './IStateSelector' import {TStateSelector} from './TStateSelector'
import {connect, Omit} from 'react-redux' import {connect, Omit} from 'react-redux'
import {Dispatch} from 'redux' import {Dispatch} from 'redux'
import {ComponentType} from 'react' import {ComponentType} from 'react'
@ -30,7 +30,7 @@ export abstract class Connector<LocalState> {
* https://stackoverflow.com/questions/54277411 * https://stackoverflow.com/questions/54277411
*/ */
abstract connect<State>( abstract connect<State>(
selectState: IStateSelector<State, LocalState>, selectState: TStateSelector<State, LocalState>,
): ComponentType<any> ): ComponentType<any>
protected wrap< protected wrap<
@ -39,7 +39,7 @@ export abstract class Connector<LocalState> {
StateProps extends Partial<Props>, StateProps extends Partial<Props>,
DispatchProps extends Partial<Props>, DispatchProps extends Partial<Props>,
>( >(
getLocalState: IStateSelector<State, LocalState>, getLocalState: TStateSelector<State, LocalState>,
mapStateToProps: (state: LocalState) => StateProps, mapStateToProps: (state: LocalState) => StateProps,
mapDispatchToProps: (dispatch: Dispatch) => DispatchProps, mapDispatchToProps: (dispatch: Dispatch) => DispatchProps,
Component: React.ComponentType<Props>, Component: React.ComponentType<Props>,

View File

@ -1,5 +1,5 @@
/* /*
* Select and return a part of the state * Select and return a part of the state
*/ */
export type IStateSelector<GlobalState, StateSlice> export type TStateSelector<GlobalState, StateSlice>
= (state: GlobalState) => StateSlice = (state: GlobalState) => StateSlice

View File

@ -1,2 +1,2 @@
export * from './Connector' export * from './Connector'
export * from './IStateSelector' export * from './TStateSelector'

View File

@ -3,13 +3,13 @@ import ReactDOM from 'react-dom'
import {Action} from 'redux' import {Action} from 'redux'
import {IClientConfig} from './IClientConfig' import {IClientConfig} from './IClientConfig'
import {IRenderer} from './IRenderer' import {IRenderer} from './IRenderer'
import {IStoreFactory} from './IStoreFactory' import {TStoreFactory} from './TStoreFactory'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
import {Router} from 'react-router-dom' import {Router} from 'react-router-dom'
import {createBrowserHistory} from 'history' import {createBrowserHistory} from 'history'
export interface IClientRendererParams<State, A extends Action> { export interface IClientRendererParams<State, A extends Action> {
readonly createStore: IStoreFactory<State, A | any>, readonly createStore: TStoreFactory<State, A | any>,
readonly RootComponent: React.ComponentType<{config: IClientConfig}>, readonly RootComponent: React.ComponentType<{config: IClientConfig}>,
readonly target?: HTMLElement readonly target?: HTMLElement
} }

View File

@ -2,7 +2,7 @@ import React from 'react'
import {Action} from 'redux' import {Action} from 'redux'
import {IClientConfig} from './IClientConfig' import {IClientConfig} from './IClientConfig'
import {IRenderer} from './IRenderer' import {IRenderer} from './IRenderer'
import {IStoreFactory} from './IStoreFactory' import {TStoreFactory} from './TStoreFactory'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
import {StaticRouterContext} from 'react-router' import {StaticRouterContext} from 'react-router'
import {StaticRouter} from 'react-router-dom' import {StaticRouter} from 'react-router-dom'
@ -10,7 +10,7 @@ import {renderToNodeStream} from 'react-dom/server'
export class ServerRenderer<State, A extends Action> implements IRenderer { export class ServerRenderer<State, A extends Action> implements IRenderer {
constructor( constructor(
readonly createStore: IStoreFactory<State, A | any>, readonly createStore: TStoreFactory<State, A | any>,
readonly RootComponent: React.ComponentType<{config: IClientConfig}>, readonly RootComponent: React.ComponentType<{config: IClientConfig}>,
) {} ) {}
render(url: string, config: IClientConfig, state?: any) { render(url: string, config: IClientConfig, state?: any) {

View File

@ -1,5 +1,5 @@
import {Action, Store} from 'redux' import {Action, Store} from 'redux'
// TODO maybe Store should also be typed // TODO maybe Store should also be typed
export type IStoreFactory<State, A extends Action> = export type TStoreFactory<State, A extends Action> =
(state?: State) => Store<State, A | any> (state?: State) => Store<State, A | any>

View File

@ -1,5 +1,5 @@
export * from './ClientRenderer' export * from './ClientRenderer'
export * from './IClientConfig' export * from './IClientConfig'
export * from './IRenderer' export * from './IRenderer'
export * from './IStoreFactory' export * from './TStoreFactory'
export * from './isClientSide' export * from './isClientSide'

View File

@ -1,19 +1,19 @@
import {IAPIDef} from '@rondo/common' import {IAPIDef} from '@rondo/common'
import {GetPendingAction, IAsyncAction, PendingAction} from '../actions' import {TGetPendingAction, TAsyncAction, PendingAction} from '../actions'
import {IHTTPClient} from '../http/IHTTPClient' import {IHTTPClient} from '../http/IHTTPClient'
import {ITeam, IUser, IUserInTeam} from '@rondo/common' import {ITeam, IUser, IUserInTeam} from '@rondo/common'
export type TeamActionType = export type TTeamAction =
IAsyncAction<ITeam[], 'TEAMS'> TAsyncAction<ITeam[], 'TEAMS'>
| IAsyncAction<ITeam, 'TEAM_CREATE'> | TAsyncAction<ITeam, 'TEAM_CREATE'>
| IAsyncAction<ITeam, 'TEAM_UPDATE'> | TAsyncAction<ITeam, 'TEAM_UPDATE'>
| IAsyncAction<{id: number}, 'TEAM_REMOVE'> | TAsyncAction<{id: number}, 'TEAM_REMOVE'>
| IAsyncAction<IUserInTeam, 'TEAM_USER_ADD'> | TAsyncAction<IUserInTeam, 'TEAM_USER_ADD'>
| IAsyncAction<{userId: number, teamId: number}, 'TEAM_USER_REMOVE'> | TAsyncAction<{userId: number, teamId: number}, 'TEAM_USER_REMOVE'>
| IAsyncAction<{teamId: number, usersInTeam: IUserInTeam[]}, 'TEAM_USERS'> | TAsyncAction<{teamId: number, usersInTeam: IUserInTeam[]}, 'TEAM_USERS'>
| IAsyncAction<IUser | undefined, 'TEAM_USER_FIND'> | TAsyncAction<IUser | undefined, 'TEAM_USER_FIND'>
type Action<T extends string> = GetPendingAction<TeamActionType, T> type Action<T extends string> = TGetPendingAction<TTeamAction, T>
export class TeamActions { export class TeamActions {
constructor(protected readonly http: IHTTPClient<IAPIDef>) {} constructor(protected readonly http: IHTTPClient<IAPIDef>) {}

View File

@ -1,5 +1,5 @@
import {Connector} from '../redux/Connector' import {Connector} from '../redux/Connector'
import {IStateSelector} from '../redux' import {TStateSelector} from '../redux'
import {ITeamState} from './TeamReducer' import {ITeamState} from './TeamReducer'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {TeamManager} from './TeamManager' import {TeamManager} from './TeamManager'
@ -11,7 +11,7 @@ export class TeamConnector extends Connector<ITeamState> {
super() super()
} }
connect<State>(getLocalState: IStateSelector<State, ITeamState>) { connect<State>(getLocalState: TStateSelector<State, ITeamState>) {
const Component = this.wrap( const Component = this.wrap(
getLocalState, getLocalState,
state => ({ state => ({

View File

@ -4,7 +4,7 @@ import {ITeam} from '@rondo/common'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {FaPlusSquare, FaCheck, FaEdit} from 'react-icons/fa' import {FaPlusSquare, FaCheck, FaEdit} from 'react-icons/fa'
export type ITeamEditorProps = { export type TTeamEditorProps = {
type: 'add' type: 'add'
onAddTeam: TeamActions['createTeam'] onAddTeam: TeamActions['createTeam']
} | { } | {
@ -20,8 +20,8 @@ export interface ITeamEditorState {
} }
export class TeamEditor export class TeamEditor
extends React.PureComponent<ITeamEditorProps, ITeamEditorState> { extends React.PureComponent<TTeamEditorProps, ITeamEditorState> {
constructor(props: ITeamEditorProps) { constructor(props: TTeamEditorProps) {
super(props) super(props)
this.state = { this.state = {
error: '', error: '',
@ -31,7 +31,7 @@ extends React.PureComponent<ITeamEditorProps, ITeamEditorState> {
getName(team?: ITeam) { getName(team?: ITeam) {
return team ? team.name : '' return team ? team.name : ''
} }
componentWillReceiveProps(nextProps: ITeamEditorProps) { componentWillReceiveProps(nextProps: TTeamEditorProps) {
if (nextProps.type === 'update') { if (nextProps.type === 'update') {
const {team} = nextProps const {team} = nextProps
if (team !== (this.props as any).team) { if (team !== (this.props as any).team) {

View File

@ -1,13 +1,13 @@
import React from 'react' import React from 'react'
import {Button, Panel, PanelHeading, PanelBlock} from 'bloomer' import {Button, Panel, PanelHeading, PanelBlock} from 'bloomer'
import {FaPlus, FaEdit, FaTimes} from 'react-icons/fa' import {FaPlus, FaEdit, FaTimes} from 'react-icons/fa'
import {ITeam, ReadonlyRecord} from '@rondo/common' import {ITeam, TReadonlyRecord} from '@rondo/common'
import {Link} from 'react-router-dom' import {Link} from 'react-router-dom'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {TeamEditor} from './TeamEditor' import {TeamEditor} from './TeamEditor'
export interface ITeamListProps { export interface ITeamListProps {
teamsById: ReadonlyRecord<number, ITeam>, teamsById: TReadonlyRecord<number, ITeam>,
teamIds: ReadonlyArray<number>, teamIds: ReadonlyArray<number>,
onAddTeam: TeamActions['createTeam'] onAddTeam: TeamActions['createTeam']
onRemoveTeam: TeamActions['removeTeam'] onRemoveTeam: TeamActions['removeTeam']

View File

@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
import {History, Location} from 'history' import {History, Location} from 'history'
import {ITeam, IUserInTeam, ReadonlyRecord} from '@rondo/common' import {ITeam, IUserInTeam, TReadonlyRecord} from '@rondo/common'
import {Panel, PanelBlock, PanelHeading} from 'bloomer' import {Panel, PanelBlock, PanelHeading} from 'bloomer'
import {Route, Switch} from 'react-router-dom' import {Route, Switch} from 'react-router-dom'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
@ -24,11 +24,11 @@ export interface ITeamManagerProps {
fetchUsersInTeam: TeamActions['fetchUsersInTeam'] fetchUsersInTeam: TeamActions['fetchUsersInTeam']
findUserByEmail: TeamActions['findUserByEmail'] findUserByEmail: TeamActions['findUserByEmail']
teamsById: ReadonlyRecord<number, ITeam> teamsById: TReadonlyRecord<number, ITeam>
teamIds: ReadonlyArray<number> teamIds: ReadonlyArray<number>
userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>> userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
usersByKey: ReadonlyRecord<string, IUserInTeam> usersByKey: TReadonlyRecord<string, IUserInTeam>
} }
export class TeamManager extends React.PureComponent<ITeamManagerProps> { export class TeamManager extends React.PureComponent<ITeamManagerProps> {

View File

@ -1,17 +1,17 @@
import { import {
ITeam, IUserInTeam, ReadonlyRecord, indexBy, without, ITeam, IUserInTeam, TReadonlyRecord, indexBy, without,
} from '@rondo/common' } from '@rondo/common'
import {TeamActionType} from './TeamActions' import {TTeamAction} from './TeamActions'
import {GetResolvedAction} from '../actions' import {TGetResolvedAction} from '../actions'
export interface ITeamState { export interface ITeamState {
readonly error: string readonly error: string
readonly teamIds: ReadonlyArray<number> readonly teamIds: ReadonlyArray<number>
readonly teamsById: ReadonlyRecord<number, ITeam> readonly teamsById: TReadonlyRecord<number, ITeam>
readonly userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>> readonly userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
readonly usersByKey: ReadonlyRecord<string, IUserInTeam> readonly usersByKey: TReadonlyRecord<string, IUserInTeam>
} }
const defaultState: ITeamState = { const defaultState: ITeamState = {
@ -26,7 +26,7 @@ const defaultState: ITeamState = {
function removeUser( function removeUser(
state: ITeamState, state: ITeamState,
action: GetResolvedAction<TeamActionType, 'TEAM_USER_REMOVE'>, action: TGetResolvedAction<TTeamAction, 'TEAM_USER_REMOVE'>,
) { ) {
const {payload} = action const {payload} = action
@ -52,7 +52,7 @@ function getUserKey(userInTeam: {userId: number, teamId: number}) {
return `${userInTeam.teamId}_${userInTeam.userId}` return `${userInTeam.teamId}_${userInTeam.userId}`
} }
export function Team(state = defaultState, action: TeamActionType): ITeamState { export function Team(state = defaultState, action: TTeamAction): ITeamState {
switch (action.status) { switch (action.status) {
case 'pending': case 'pending':
return state return state

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {ITeam, IUser, IUserInTeam, ReadonlyRecord} from '@rondo/common' import {ITeam, IUser, IUserInTeam, TReadonlyRecord} from '@rondo/common'
import {TeamActions} from './TeamActions' import {TeamActions} from './TeamActions'
import {FaUser, FaCheck, FaTimes} from 'react-icons/fa' import {FaUser, FaCheck, FaTimes} from 'react-icons/fa'
@ -18,8 +18,8 @@ export interface ITeamUsersProps {
onRemoveUser: TeamActions['removeUser'] onRemoveUser: TeamActions['removeUser']
team: ITeam team: ITeam
userKeysByTeamId: ReadonlyRecord<number, ReadonlyArray<string>> userKeysByTeamId: TReadonlyRecord<number, ReadonlyArray<string>>
usersByKey: ReadonlyRecord<string, IUserInTeam> usersByKey: TReadonlyRecord<string, IUserInTeam>
} }
export interface ITeamUserProps { export interface ITeamUserProps {

View File

@ -1,7 +1,7 @@
import React from 'react' import React from 'react'
import ReactDOM from 'react-dom' import ReactDOM from 'react-dom'
import T from 'react-dom/test-utils' import T from 'react-dom/test-utils'
import {IStateSelector} from '../redux' import {TStateSelector} from '../redux'
import {Provider} from 'react-redux' import {Provider} from 'react-redux'
import {createStore} from '../store' import {createStore} from '../store'
import { import {
@ -15,9 +15,9 @@ import {
interface IRenderParams<State, LocalState> { interface IRenderParams<State, LocalState> {
reducers: ReducersMapObject<State, any> reducers: ReducersMapObject<State, any>
select: IStateSelector<State, LocalState> select: TStateSelector<State, LocalState>
// getComponent: ( // getComponent: (
// select: IStateSelector<State, LocalState>) => React.ComponentType<Props>, // select: TStateSelector<State, LocalState>) => React.ComponentType<Props>,
// customJSX?: ( // customJSX?: (
// Component: React.ComponentType<Props>, // Component: React.ComponentType<Props>,
// props: Props, // props: Props,
@ -65,7 +65,7 @@ export class TestUtils {
} }
const withComponent = <Props extends {}>( const withComponent = <Props extends {}>(
getComponent: (select: IStateSelector<State, LocalState>) => getComponent: (select: TStateSelector<State, LocalState>) =>
React.ComponentType<Props>, React.ComponentType<Props>,
) => { ) => {
const Component = getComponent(select) const Component = getComponent(select)

View File

@ -1,4 +1,4 @@
export type IMethod = 'get' export type TMethod = 'get'
| 'post' | 'post'
| 'put' | 'put'
| 'delete' | 'delete'

View File

@ -1,2 +0,0 @@
export type ReadonlyRecord<K extends string | number | symbol, V> =
Readonly<Record<K, V>>

View File

@ -9,7 +9,6 @@ export * from './IUser'
export * from './IUser' export * from './IUser'
export * from './IUserInTeam' export * from './IUserInTeam'
export * from './IUserTeam' export * from './IUserTeam'
export * from './ReadonlyRecord'
export * from './URLFormatter' export * from './URLFormatter'
export * from './indexBy' export * from './indexBy'
export * from './types' export * from './types'

View File

@ -1,22 +1,25 @@
/** /**
* transform unknown into undefined * transform unknown into undefined
*/ */
export type Optional<T> = T extends {} ? T : undefined export type TOptional<T> = T extends {} ? T : undefined
export type NonUndefinedPropertyNames<T> = { export type TNonUndefinedPropertyNames<T> = {
[K in keyof T]: T[K] extends undefined ? never: K [K in keyof T]: T[K] extends undefined ? never: K
}[keyof T] }[keyof T]
export type OnlyRequired<T> = Pick<T, NonUndefinedPropertyNames<T>> export type TOnlyRequired<T> = Pick<T, TNonUndefinedPropertyNames<T>>
export type NonUnknownPropertyNames<T> = { export type TNonUnknownPropertyNames<T> = {
[K in keyof T]: T[K] extends {} ? K : never [K in keyof T]: T[K] extends {} ? K : never
}[keyof T] }[keyof T]
export type OnlyDefined<T> = Pick<T, NonUnknownPropertyNames<T>> export type TOnlyDefined<T> = Pick<T, TNonUnknownPropertyNames<T>>
/** /**
* Remove types from T that are not assignable to U * Remove types from T that are not assignable to U
* https://www.typescriptlang.org/docs/handbook/advanced-types.html * https://www.typescriptlang.org/docs/handbook/advanced-types.html
*/ */
export type Filter<T, U> = T extends U ? T : never export type TFilter<T, U> = T extends U ? T : never
export type TReadonlyRecord<K extends string | number | symbol, V> =
Readonly<Record<K, V>>

View File

@ -8,12 +8,12 @@ import {
Repository, Repository,
} from 'typeorm' } from 'typeorm'
export type IConnectionGetter = () => Connection export type TConnectionGetter = () => Connection
export class TransactionManager implements ITransactionManager { export class TransactionManager implements ITransactionManager {
constructor( constructor(
readonly ns: Namespace, readonly ns: Namespace,
readonly getConnection: IConnectionGetter, readonly getConnection: TConnectionGetter,
) {} ) {}
getEntityManager = (): EntityManager => { getEntityManager = (): EntityManager => {

View File

@ -17,10 +17,10 @@ export function pad(text: string, n: number, trim: boolean) {
return text return text
} }
export type ILogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose' | 'off' export type TLogLevel = 'error' | 'warn' | 'info' | 'debug' | 'verbose' | 'off'
export interface IEnabledLoggers { export interface IEnabledLoggers {
readonly [key: string]: ILogLevel readonly [key: string]: TLogLevel
} }
export interface IParams { export interface IParams {
@ -48,9 +48,9 @@ export class LoggerFactory implements ILoggerFactory {
} = {}) { } = {}) {
const enabledLoggers = logs.split(',').reduce((logConfig, log) => { const enabledLoggers = logs.split(',').reduce((logConfig, log) => {
const [key, value] = log.split(':') const [key, value] = log.split(':')
logConfig[key] = (value || 'info') as ILogLevel logConfig[key] = (value || 'info') as TLogLevel
return logConfig return logConfig
}, {} as {[key: string]: ILogLevel}) }, {} as {[key: string]: TLogLevel})
const params = opts.split(',').reduce((o, key) => { const params = opts.split(',').reduce((o, key) => {
o[key] = true o[key] = true
@ -70,7 +70,7 @@ export class LoggerFactory implements ILoggerFactory {
this.getCorrelationId = () => '' this.getCorrelationId = () => ''
} }
getLoggerLevel(name: string): ILogLevel { getLoggerLevel(name: string): TLogLevel {
const {enabledLoggers} = this.options const {enabledLoggers} = this.options
const disabled = !!enabledLoggers['-' + name] const disabled = !!enabledLoggers['-' + name]
if (disabled) { if (disabled) {

View File

@ -1,13 +1,13 @@
import {Authenticator as A, Passport} from 'passport' import {Authenticator as A, Passport} from 'passport'
import {IUserService} from '../services' import {IUserService} from '../services'
import {Strategy as LocalStrategy} from 'passport-local' import {Strategy as LocalStrategy} from 'passport-local'
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {IMiddleware} from './IMiddleware' import {IMiddleware} from './IMiddleware'
export class Authenticator implements IMiddleware { export class Authenticator implements IMiddleware {
protected readonly passport: A protected readonly passport: A
readonly handle: IHandler[] readonly handle: THandler[]
constructor(protected readonly userService: IUserService) { constructor(protected readonly userService: IUserService) {
this.passport = new Passport() as any this.passport = new Passport() as any
@ -22,7 +22,7 @@ export class Authenticator implements IMiddleware {
] ]
} }
withLogInPromise: IHandler = (req, res, next) => { withLogInPromise: THandler = (req, res, next) => {
req.logInPromise = (user) => { req.logInPromise = (user) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
req.logIn(user, err => { req.logIn(user, err => {
@ -72,7 +72,7 @@ export class Authenticator implements IMiddleware {
.catch(done) .catch(done)
} }
authenticate(strategy: string | string[]): IHandler { authenticate(strategy: string | string[]): THandler {
return (req, res, next) => { return (req, res, next) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.passport.authenticate(strategy, (err: Error, user, info) => { this.passport.authenticate(strategy, (err: Error, user, info) => {

View File

@ -1,5 +1,5 @@
import Csurf from 'csurf' import Csurf from 'csurf'
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {IMiddleware} from './IMiddleware' import {IMiddleware} from './IMiddleware'
import {UrlWithStringQuery} from 'url' import {UrlWithStringQuery} from 'url'
@ -9,7 +9,7 @@ export interface ICSRFParams {
} }
export class CSRFMiddleware implements IMiddleware { export class CSRFMiddleware implements IMiddleware {
readonly handle: IHandler readonly handle: THandler
constructor(readonly params: ICSRFParams) { constructor(readonly params: ICSRFParams) {
this.handle = Csurf({ this.handle = Csurf({

View File

@ -1,4 +1,4 @@
import {IErrorHandler} from './IErrorHandler' import {TErrorHandler} from './TErrorHandler'
import {ILogger} from '../logger/ILogger' import {ILogger} from '../logger/ILogger'
import {IMiddleware} from './IMiddleware' import {IMiddleware} from './IMiddleware'
import {ValidationError} from '../validator' import {ValidationError} from '../validator'
@ -6,7 +6,7 @@ import {ValidationError} from '../validator'
export class ErrorApiHandler implements IMiddleware { export class ErrorApiHandler implements IMiddleware {
constructor(readonly logger: ILogger) {} constructor(readonly logger: ILogger) {}
handle: IErrorHandler = (err, req, res, next) => { handle: TErrorHandler = (err, req, res, next) => {
this.logger.error('%s An API error occurred: %s', this.logger.error('%s An API error occurred: %s',
req.correlationId, err.stack) req.correlationId, err.stack)
const statusCode = this.getStatus(err) const statusCode = this.getStatus(err)

View File

@ -1,11 +1,11 @@
import {IMiddleware} from './IMiddleware'
import {IErrorHandler} from './IErrorHandler'
import {ILogger} from '../logger/ILogger' import {ILogger} from '../logger/ILogger'
import {IMiddleware} from './IMiddleware'
import {TErrorHandler} from './TErrorHandler'
export class ErrorPageHandler implements IMiddleware { export class ErrorPageHandler implements IMiddleware {
constructor(readonly logger: ILogger) {} constructor(readonly logger: ILogger) {}
handle: IErrorHandler = (err, req, res, next) => { handle: TErrorHandler = (err, req, res, next) => {
this.logger.error( this.logger.error(
'%s An error occurred: %s', '%s An error occurred: %s',
req.correlationId, err.stack) req.correlationId, err.stack)

View File

@ -1,6 +1,6 @@
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {IErrorHandler} from './IErrorHandler' import {TErrorHandler} from './TErrorHandler'
export interface IMiddleware { export interface IMiddleware {
handle: IHandler | IHandler[] | IErrorHandler handle: THandler | THandler[] | TErrorHandler
} }

View File

@ -1,4 +1,4 @@
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {ILogger} from '../logger/ILogger' import {ILogger} from '../logger/ILogger'
import {IMiddleware} from './IMiddleware' import {IMiddleware} from './IMiddleware'
import shortid from 'shortid' import shortid from 'shortid'
@ -6,7 +6,7 @@ import shortid from 'shortid'
export class RequestLogger implements IMiddleware { export class RequestLogger implements IMiddleware {
constructor(protected readonly logger: ILogger) {} constructor(protected readonly logger: ILogger) {}
handle: IHandler = (req, res, next) => { handle: THandler = (req, res, next) => {
const start = Date.now() const start = Date.now()
req.correlationId = shortid.generate() req.correlationId = shortid.generate()
res.on('finish', () => { res.on('finish', () => {

View File

@ -1,5 +1,5 @@
import ExpressSession from 'express-session' import ExpressSession from 'express-session'
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {IMiddleware} from './IMiddleware' import {IMiddleware} from './IMiddleware'
import {ISession} from '../session/ISession' import {ISession} from '../session/ISession'
import {ITransactionManager} from '../database/ITransactionManager' import {ITransactionManager} from '../database/ITransactionManager'
@ -15,7 +15,7 @@ export interface ISessionOptions {
} }
export class SessionMiddleware implements IMiddleware { export class SessionMiddleware implements IMiddleware {
readonly handle: IHandler readonly handle: THandler
constructor(readonly params: ISessionOptions) { constructor(readonly params: ISessionOptions) {
this.handle = ExpressSession({ this.handle = ExpressSession({

View File

@ -1,4 +1,4 @@
import {Request, Response, NextFunction} from 'express' import {Request, Response, NextFunction} from 'express'
export type IErrorHandler = export type TErrorHandler =
(err: Error, req: Request, res: Response, next: NextFunction) => any (err: Error, req: Request, res: Response, next: NextFunction) => any

View File

@ -1,3 +1,3 @@
import {Request, Response, NextFunction} from 'express' import {Request, Response, NextFunction} from 'express'
export type IHandler = (req: Request, res: Response, next: NextFunction) => any export type THandler = (req: Request, res: Response, next: NextFunction) => any

View File

@ -1,4 +1,4 @@
import {Request, Response, NextFunction} from 'express' import {Request, Response, NextFunction} from 'express'
export type IPromiseHandler<T> = export type TPromiseHandler<T> =
(req: Request, res: Response, next: NextFunction) => Promise<T> (req: Request, res: Response, next: NextFunction) => Promise<T>

View File

@ -1,6 +1,6 @@
import shortid from 'shortid' import shortid from 'shortid'
import {IMiddleware} from './IMiddleware' import {IMiddleware} from './IMiddleware'
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {Namespace} from 'cls-hooked' import {Namespace} from 'cls-hooked'
export const CORRELATION_ID = 'CORRELATION_ID' export const CORRELATION_ID = 'CORRELATION_ID'
@ -8,7 +8,7 @@ export const CORRELATION_ID = 'CORRELATION_ID'
export class Transaction implements IMiddleware { export class Transaction implements IMiddleware {
constructor(readonly ns: Namespace) {} constructor(readonly ns: Namespace) {}
handle: IHandler = (req, res, next) => { handle: THandler = (req, res, next) => {
const {ns} = this const {ns} = this
ns.bindEmitter(req) ns.bindEmitter(req)
ns.bindEmitter(res) ns.bindEmitter(res)

View File

@ -1,10 +1,10 @@
import createError from 'http-errors' import createError from 'http-errors'
import {Request} from 'express' import {Request} from 'express'
import {IHandler} from './IHandler' import {THandler} from './THandler'
const isLoggedIn = (req: Request) => !!(req as any).user const isLoggedIn = (req: Request) => !!(req as any).user
export const ensureLoggedInApi: IHandler = (req, res, next) => { export const ensureLoggedInApi: THandler = (req, res, next) => {
if (!isLoggedIn(req)) { if (!isLoggedIn(req)) {
next(createError(401)) next(createError(401))
return return
@ -12,7 +12,7 @@ export const ensureLoggedInApi: IHandler = (req, res, next) => {
next() next()
} }
export const ensureLoggedInSite = (redirectTo: string): IHandler => { export const ensureLoggedInSite = (redirectTo: string): THandler => {
return function _ensureLoggedInSite(req, res, next) { return function _ensureLoggedInSite(req, res, next) {
if (!isLoggedIn(req)) { if (!isLoggedIn(req)) {
res.redirect(redirectTo) res.redirect(redirectTo)

View File

@ -1,7 +1,7 @@
import {IHandler} from './IHandler' import {THandler} from './THandler'
import {IPromiseHandler} from './IPromiseHandler' import {TPromiseHandler} from './TPromiseHandler'
export function handlePromise<T>(endpoint: IPromiseHandler<T>): IHandler { export function handlePromise<T>(endpoint: TPromiseHandler<T>): THandler {
return (req, res, next) => { return (req, res, next) => {
const promise = endpoint(req, res, next) const promise = endpoint(req, res, next)
promise promise

View File

@ -1,13 +1,13 @@
export * from './Authenticator' export * from './Authenticator'
export * from './CSRFMiddleware' export * from './CSRFMiddleware'
export * from './ensureLoggedIn'
export * from './ErrorApiHandler' export * from './ErrorApiHandler'
export * from './ErrorPageHandler' export * from './ErrorPageHandler'
export * from './handlePromise'
export * from './IErrorHandler'
export * from './IHandler'
export * from './IMiddleware' export * from './IMiddleware'
export * from './IPromiseHandler'
export * from './RequestLogger' export * from './RequestLogger'
export * from './SessionMiddleware' export * from './SessionMiddleware'
export * from './TErrorHandler'
export * from './THandler'
export * from './TPromiseHandler'
export * from './Transaction' export * from './Transaction'
export * from './ensureLoggedIn'
export * from './handlePromise'

View File

@ -1,6 +1,6 @@
import express from 'express' import express from 'express'
import {IRoutes, IMethod} from '@rondo/common' import {IRoutes, TMethod} from '@rondo/common'
import {ITypedHandler} from './ITypedHandler' import {TTypedHandler} from './TTypedHandler'
export class AsyncRouter<R extends IRoutes> { export class AsyncRouter<R extends IRoutes> {
readonly router: express.Router readonly router: express.Router
@ -11,18 +11,18 @@ export class AsyncRouter<R extends IRoutes> {
this.use = this.router.use.bind(this.router) as any this.use = this.router.use.bind(this.router) as any
} }
protected addRoute<M extends IMethod, P extends keyof R & string>( protected addRoute<M extends TMethod, P extends keyof R & string>(
method: M, method: M,
path: P, path: P,
handler: ITypedHandler<R, P, M>, handler: TTypedHandler<R, P, M>,
) { ) {
const addRoute = this.router[method].bind(this.router as any) const addRoute = this.router[method].bind(this.router as any)
addRoute(path, this.wrapHandler(handler)) addRoute(path, this.wrapHandler(handler))
} }
protected wrapHandler<M extends IMethod, P extends keyof R & string>( protected wrapHandler<M extends TMethod, P extends keyof R & string>(
handler: ITypedHandler<R, P, M>, handler: TTypedHandler<R, P, M>,
): express.RequestHandler { ): express.RequestHandler {
return (req, res, next) => { return (req, res, next) => {
handler(req, res, next) handler(req, res, next)
@ -35,43 +35,43 @@ export class AsyncRouter<R extends IRoutes> {
get<P extends keyof R & string>( get<P extends keyof R & string>(
path: P, path: P,
handler: ITypedHandler<R, P, 'get'>, handler: TTypedHandler<R, P, 'get'>,
) { ) {
this.addRoute('get', path, handler) this.addRoute('get', path, handler)
} }
post<P extends keyof R & string>( post<P extends keyof R & string>(
path: P, path: P,
handler: ITypedHandler<R, P, 'post'>, handler: TTypedHandler<R, P, 'post'>,
) { ) {
this.addRoute('post', path, handler) this.addRoute('post', path, handler)
} }
put<P extends keyof R & string>( put<P extends keyof R & string>(
path: P, handler: ITypedHandler<R, P, 'put'>, path: P, handler: TTypedHandler<R, P, 'put'>,
) { ) {
this.addRoute('put', path, handler) this.addRoute('put', path, handler)
} }
delete<P extends keyof R & string>( delete<P extends keyof R & string>(
path: P, handler: ITypedHandler<R, P, 'delete'>) { path: P, handler: TTypedHandler<R, P, 'delete'>) {
this.addRoute('delete', path, handler) this.addRoute('delete', path, handler)
} }
head<P extends keyof R & string>( head<P extends keyof R & string>(
path: P, path: P,
handler: ITypedHandler<R, P, 'head'>, handler: TTypedHandler<R, P, 'head'>,
) { ) {
this.addRoute('head', path, handler) this.addRoute('head', path, handler)
} }
options<P extends keyof R & string>( options<P extends keyof R & string>(
path: P, handler: ITypedHandler<R, P, 'options'>) { path: P, handler: TTypedHandler<R, P, 'options'>) {
this.addRoute('options', path, handler) this.addRoute('options', path, handler)
} }
patch<P extends keyof R & string>( patch<P extends keyof R & string>(
path: P, handler: ITypedHandler<R, P, 'patch'>) { path: P, handler: TTypedHandler<R, P, 'patch'>) {
this.addRoute('patch', path, handler) this.addRoute('patch', path, handler)
} }

View File

@ -1,11 +1,11 @@
import express from 'express' import express from 'express'
import {IRoutes, IMethod} from '@rondo/common' import {IRoutes, TMethod} from '@rondo/common'
import {ITypedRequest} from './ITypedRequest' import {ITypedRequest} from './ITypedRequest'
export type ITypedHandler< export type TTypedHandler<
R extends IRoutes, R extends IRoutes,
P extends keyof R, P extends keyof R,
M extends IMethod M extends TMethod
> = ( > = (
req: ITypedRequest<R[P][M]>, req: ITypedRequest<R[P][M]>,
res: express.Response, res: express.Response,

View File

@ -1,16 +1,16 @@
import express from 'express' import express from 'express'
import {AsyncRouter} from './AsyncRouter' import {AsyncRouter} from './AsyncRouter'
import {IRoutes, IMethod} from '@rondo/common' import {IRoutes, TMethod} from '@rondo/common'
import {ITransactionManager} from '../database/ITransactionManager' import {ITransactionManager} from '../database/ITransactionManager'
import {ITypedHandler} from './ITypedHandler' import {TTypedHandler} from './TTypedHandler'
export class TransactionalRouter<R extends IRoutes> extends AsyncRouter<R> { export class TransactionalRouter<R extends IRoutes> extends AsyncRouter<R> {
constructor(readonly transactionManager: ITransactionManager) { constructor(readonly transactionManager: ITransactionManager) {
super() super()
} }
protected wrapHandler<M extends IMethod, P extends keyof R & string>( protected wrapHandler<M extends TMethod, P extends keyof R & string>(
handler: ITypedHandler<R, P, M>, handler: TTypedHandler<R, P, M>,
): express.RequestHandler { ): express.RequestHandler {
return async (req, res, next) => { return async (req, res, next) => {
await this.transactionManager await this.transactionManager

View File

@ -1,4 +1,4 @@
export * from './AsyncRouter' export * from './AsyncRouter'
export * from './ITypedHandler' export * from './TTypedHandler'
export * from './ITypedRequest' export * from './ITypedRequest'
export * from './TransactionalRouter' export * from './TransactionalRouter'

View File

@ -1,9 +1,9 @@
import {IHandler} from '../middleware/IHandler' import {THandler} from '../middleware/THandler'
import {AsyncRouter} from '../router' import {AsyncRouter} from '../router'
import {IRoutes} from '@rondo/common' import {IRoutes} from '@rondo/common'
export abstract class BaseRoute<T extends IRoutes> { export abstract class BaseRoute<T extends IRoutes> {
readonly handle: IHandler readonly handle: THandler
constructor(protected readonly t: AsyncRouter<T>) { constructor(protected readonly t: AsyncRouter<T>) {
this.handle = t.router this.handle = t.router

View File

@ -9,11 +9,11 @@ type CallbackErr = (err?: any) => void
export interface ISessionStoreOptions<S extends ISession> { export interface ISessionStoreOptions<S extends ISession> {
readonly ttl: number readonly ttl: number
readonly cleanup: number readonly cleanup: number
readonly getRepository: IRepositoryFactory<S> readonly getRepository: TRepositoryFactory<S>
buildSession(sessionData: SessionData, session: ISession): S buildSession(sessionData: SessionData, session: ISession): S
} }
export type IRepositoryFactory<T> = () => Repository<T> export type TRepositoryFactory<T> = () => Repository<T>
// TODO casting as any because TypeScript complains. Looks like this is a // TODO casting as any because TypeScript complains. Looks like this is a
// bug in TypeScript 3.2.2 // bug in TypeScript 3.2.2
@ -22,7 +22,7 @@ export type IRepositoryFactory<T> = () => Repository<T>
// https://github.com/Microsoft/TypeScript/issues/21592 // https://github.com/Microsoft/TypeScript/issues/21592
export class SessionStore<S extends ISession> extends Store { export class SessionStore<S extends ISession> extends Store {
protected readonly getRepository: IRepositoryFactory<S> protected readonly getRepository: TRepositoryFactory<S>
constructor( constructor(
protected readonly options: ISessionStoreOptions<S>, protected readonly options: ISessionStoreOptions<S>,

View File

@ -1,6 +1,6 @@
import supertest from 'supertest' import supertest from 'supertest'
import { import {
IMethod, TMethod,
IRoutes, IRoutes,
URLFormatter, URLFormatter,
} from '@rondo/common' } from '@rondo/common'
@ -13,7 +13,7 @@ interface ITest extends Omit<Omit<supertest.Test, 'then'>, 'catch'> {}
interface IResponse< interface IResponse<
R extends IRoutes, R extends IRoutes,
P extends keyof R, P extends keyof R,
M extends IMethod, M extends TMethod,
> extends supertest.Response { > extends supertest.Response {
body: R[P][M]['response'] body: R[P][M]['response']
header: {[key: string]: string} header: {[key: string]: string}
@ -22,7 +22,7 @@ interface IResponse<
interface IRequest< interface IRequest<
R extends IRoutes, R extends IRoutes,
P extends keyof R, P extends keyof R,
M extends IMethod, M extends TMethod,
> extends ITest, Promise<IResponse<R, P, M>> { > extends ITest, Promise<IResponse<R, P, M>> {
send(value: R[P][M]['body'] | string): this send(value: R[P][M]['body'] | string): this
expect(status: number, body?: any): this expect(status: number, body?: any): this
@ -37,7 +37,7 @@ interface IRequest<
interface IRequestOptions< interface IRequestOptions<
R extends IRoutes, R extends IRoutes,
P extends keyof R, P extends keyof R,
M extends IMethod, M extends TMethod,
> { > {
params?: R[P][M]['params'], params?: R[P][M]['params'],
query?: R[P][M]['query'], query?: R[P][M]['query'],
@ -62,7 +62,7 @@ export class RequestTester<R extends IRoutes> {
return this return this
} }
request<M extends IMethod, P extends keyof R & string>( request<M extends TMethod, P extends keyof R & string>(
method: M, path: P, options: IRequestOptions<R, P, 'post'> = {}, method: M, path: P, options: IRequestOptions<R, P, 'post'> = {},
) )
: IRequest<R, P, M> { : IRequest<R, P, M> {