Fix linting errors in packages/jsonrpc
This commit is contained in:
parent
8853c71a0d
commit
50d36f268f
@ -11,6 +11,10 @@ rules:
|
|||||||
- warn
|
- warn
|
||||||
- code: 80
|
- code: 80
|
||||||
ignorePattern: '^import .* from '
|
ignorePattern: '^import .* from '
|
||||||
|
comma-dangle:
|
||||||
|
- warn
|
||||||
|
- always-multiline
|
||||||
|
|
||||||
# semi:
|
# semi:
|
||||||
# - warn
|
# - warn
|
||||||
# - never
|
# - never
|
||||||
|
|||||||
8
packages/jsonrpc/.eslintrc.yaml
Normal file
8
packages/jsonrpc/.eslintrc.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
extends:
|
||||||
|
- ../../.eslintrc.yaml
|
||||||
|
rules:
|
||||||
|
# due to the nature of this project, writing code becomes much easier
|
||||||
|
# when any can be used in some places
|
||||||
|
'@typescript-eslint/no-explicit-any': off
|
||||||
|
prefer-rest-params: off
|
||||||
|
prefer-spread: off
|
||||||
@ -8,30 +8,30 @@ import {json} from 'body-parser'
|
|||||||
|
|
||||||
describe('util', () => {
|
describe('util', () => {
|
||||||
|
|
||||||
interface IS1 {
|
interface S1 {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IS2 {
|
interface S2 {
|
||||||
mul(a: number, b: number): number
|
mul(a: number, b: number): number
|
||||||
concat(...str: string[]): string
|
concat(...str: string[]): string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IContext {
|
interface Context {
|
||||||
userId: number
|
userId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
class Service1 implements WithContext<IS1, IContext> {
|
class Service1 implements WithContext<S1, Context> {
|
||||||
add(cx: IContext, a: number, b: number) {
|
add(cx: Context, a: number, b: number) {
|
||||||
return a + b + cx.userId
|
return a + b + cx.userId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Service2 implements WithContext<IS2, IContext> {
|
class Service2 implements WithContext<S2, Context> {
|
||||||
mul(cx: IContext, a: number, b: number) {
|
mul(cx: Context, a: number, b: number) {
|
||||||
return a * b + cx.userId
|
return a * b + cx.userId
|
||||||
}
|
}
|
||||||
concat(cx: IContext, ...str: string[]) {
|
concat(cx: Context, ...str: string[]) {
|
||||||
return str.join('') + cx.userId
|
return str.join('') + cx.userId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ describe('util', () => {
|
|||||||
services,
|
services,
|
||||||
)
|
)
|
||||||
const app = createApp(router)
|
const app = createApp(router)
|
||||||
const client = createClient<IS1>(app, '/rpc/s1')
|
const client = createClient<S1>(app, '/rpc/s1')
|
||||||
const result: number = await client.add(1, 3)
|
const result: number = await client.add(1, 3)
|
||||||
expect(result).toBe(14)
|
expect(result).toBe(14)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {IJSONRPCReturnType} from './express'
|
/* eslint @typescript-eslint/no-explicit-any: off */
|
||||||
|
import {RPCReturnType} from './express'
|
||||||
import {WithContext, RPCClient, RPCActions} from './types'
|
import {WithContext, RPCClient, RPCActions} from './types'
|
||||||
import {createActions} from './redux'
|
import {createActions} from './redux'
|
||||||
import {createLocalClient, LocalClient} from './local'
|
import {createLocalClient, LocalClient} from './local'
|
||||||
@ -39,7 +40,7 @@ export function bulkCreateActions<T extends Record<string, RPCClient<any>>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function bulkjsonrpc<T>(
|
export function bulkjsonrpc<T>(
|
||||||
jsonrpc: IJSONRPCReturnType,
|
jsonrpc: RPCReturnType,
|
||||||
services: T,
|
services: T,
|
||||||
) {
|
) {
|
||||||
keys(services).forEach(key => {
|
keys(services).forEach(key => {
|
||||||
|
|||||||
@ -2,13 +2,13 @@ import createClientMock from './createClientMock'
|
|||||||
|
|
||||||
describe('createClientMock', () => {
|
describe('createClientMock', () => {
|
||||||
|
|
||||||
interface IService {
|
interface Service {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
concat(a: string, b: string): string
|
concat(a: string, b: string): string
|
||||||
}
|
}
|
||||||
|
|
||||||
it('creates a mock for all methods', async () => {
|
it('creates a mock for all methods', async () => {
|
||||||
const [client, mock] = createClientMock<IService>(['add', 'concat'])
|
const [client, mock] = createClientMock<Service>(['add', 'concat'])
|
||||||
mock.add.mockReturnValue(Promise.resolve(3))
|
mock.add.mockReturnValue(Promise.resolve(3))
|
||||||
mock.concat.mockImplementation((a, b) => Promise.resolve(a + b))
|
mock.concat.mockImplementation((a, b) => Promise.resolve(a + b))
|
||||||
expect(await client.add(4, 5)).toBe(3)
|
expect(await client.add(4, 5)).toBe(3)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { FunctionPropertyNames, RPCClient } from './types'
|
import { FunctionPropertyNames, RPCClient } from './types'
|
||||||
|
|
||||||
export type TMocked<T> = {
|
export type RPCClientMock<T> = {
|
||||||
[K in keyof T]:
|
[K in keyof T]:
|
||||||
T[K] extends (...args: infer A) => infer R
|
T[K] extends (...args: infer A) => infer R
|
||||||
? jest.Mock<R, A>
|
? jest.Mock<R, A>
|
||||||
@ -20,7 +20,7 @@ export type TMocked<T> = {
|
|||||||
*/
|
*/
|
||||||
export default function createClientMock<T extends object>(
|
export default function createClientMock<T extends object>(
|
||||||
methods: Array<FunctionPropertyNames<T>>,
|
methods: Array<FunctionPropertyNames<T>>,
|
||||||
): [RPCClient<T>, TMocked<RPCClient<T>>] {
|
): [RPCClient<T>, RPCClientMock<RPCClient<T>>] {
|
||||||
const client = methods
|
const client = methods
|
||||||
.reduce((obj, prop) => {
|
.reduce((obj, prop) => {
|
||||||
obj[prop] = jest.fn()
|
obj[prop] = jest.fn()
|
||||||
@ -29,6 +29,6 @@ export default function createClientMock<T extends object>(
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
client as RPCClient<T>,
|
client as RPCClient<T>,
|
||||||
client as TMocked<RPCClient<T>>,
|
client as RPCClientMock<RPCClient<T>>,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,15 +7,15 @@ import {
|
|||||||
|
|
||||||
describe('ensure', () => {
|
describe('ensure', () => {
|
||||||
|
|
||||||
interface IContext {
|
interface Context {
|
||||||
userId: number
|
userId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const validate: Validate<IContext> = c => c.userId > 0
|
const validate: Validate<Context> = c => c.userId > 0
|
||||||
|
|
||||||
it('decorates class methods', () => {
|
it('decorates class methods', () => {
|
||||||
class Service {
|
class Service {
|
||||||
@ensure<IContext>(validate)
|
@ensure<Context>(validate)
|
||||||
fetchData() {
|
fetchData() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ describe('ensure', () => {
|
|||||||
|
|
||||||
it('works with properties/instance method definitions', () => {
|
it('works with properties/instance method definitions', () => {
|
||||||
class Service {
|
class Service {
|
||||||
@ensure<IContext>(validate)
|
@ensure<Context>(validate)
|
||||||
fetchData = () => 1
|
fetchData = () => 1
|
||||||
}
|
}
|
||||||
const s = new Service()
|
const s = new Service()
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint @typescript-eslint/no-unused-vars: 0 */
|
||||||
import 'reflect-metadata'
|
import 'reflect-metadata'
|
||||||
|
|
||||||
export const ensureKey = Symbol('ensure')
|
export const ensureKey = Symbol('ensure')
|
||||||
@ -7,10 +8,11 @@ export type Validate<Context> = (context: Context) => boolean | Promise<boolean>
|
|||||||
|
|
||||||
export function ensure<Context>(
|
export function ensure<Context>(
|
||||||
validate: Validate<Context>,
|
validate: Validate<Context>,
|
||||||
message: string = 'Validation failed',
|
message = 'Validation failed',
|
||||||
) {
|
) {
|
||||||
return function ensureImpl(
|
return function ensureImpl(
|
||||||
target: any, key?: string, descriptor?: PropertyDescriptor) {
|
target: any, key?: string, descriptor?: PropertyDescriptor,
|
||||||
|
) {
|
||||||
switch (arguments.length) {
|
switch (arguments.length) {
|
||||||
case 1:
|
case 1:
|
||||||
return ensureClass(validate, message).apply(null, arguments as any)
|
return ensureClass(validate, message).apply(null, arguments as any)
|
||||||
@ -25,7 +27,7 @@ export function ensure<Context>(
|
|||||||
|
|
||||||
function ensureClass<Context>(
|
function ensureClass<Context>(
|
||||||
validate: Validate<Context>,
|
validate: Validate<Context>,
|
||||||
message: string = 'Validation failed',
|
message = 'Validation failed',
|
||||||
) {
|
) {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
return (target: any) => {
|
return (target: any) => {
|
||||||
@ -40,7 +42,7 @@ function ensureClass<Context>(
|
|||||||
|
|
||||||
function ensureMethod<Context>(
|
function ensureMethod<Context>(
|
||||||
validate: Validate<Context>,
|
validate: Validate<Context>,
|
||||||
message: string = 'Validation failed',
|
message = 'Validation failed',
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
target: any,
|
target: any,
|
||||||
|
|||||||
@ -1,44 +1,44 @@
|
|||||||
export interface IError {
|
export interface ErrorWithCode {
|
||||||
code: number
|
code: number
|
||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IErrorWithData<T> extends IError {
|
export interface ErrorWithData<T> extends ErrorWithCode {
|
||||||
data: T
|
data: T
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IErrorResponse<T> {
|
export interface ErrorResponse<T> {
|
||||||
jsonrpc: '2.0'
|
jsonrpc: '2.0'
|
||||||
id: string | number | null
|
id: string | number | null
|
||||||
result: null
|
result: null
|
||||||
error: IErrorWithData<T>
|
error: ErrorWithData<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IJSONRPCError<T> extends Error {
|
export interface RPCError<T> extends Error {
|
||||||
code: number
|
code: number
|
||||||
statusCode: number
|
statusCode: number
|
||||||
response: IErrorResponse<T>
|
response: ErrorResponse<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isJSONRPCError(err: any): err is IJSONRPCError<unknown> {
|
export function isRPCError(err: any): err is RPCError<unknown> {
|
||||||
return err.name === 'IJSONRPCError' &&
|
return err.name === 'RPCError' &&
|
||||||
typeof err.message === 'string' &&
|
typeof err.message === 'string' &&
|
||||||
err.hasOwnProperty('code') &&
|
Object.prototype.hasOwnProperty.call(err, 'code') &&
|
||||||
err.hasOwnProperty('response')
|
Object.prototype.hasOwnProperty.call(err, 'response')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createError<T = null>(
|
export function createError<T = null>(
|
||||||
error: IError,
|
error: ErrorWithCode,
|
||||||
info: {
|
info: {
|
||||||
id: number | string | null
|
id: number | string | null
|
||||||
data: T
|
data: T
|
||||||
statusCode: number
|
statusCode: number
|
||||||
},
|
},
|
||||||
): IJSONRPCError<T> {
|
): RPCError<T> {
|
||||||
|
|
||||||
const err = new Error(error.message) as IJSONRPCError<T>
|
const err = new Error(error.message) as RPCError<T>
|
||||||
|
|
||||||
err.name = 'IJSONRPCError'
|
err.name = 'RPCError'
|
||||||
err.code = error.code
|
err.code = error.code
|
||||||
err.statusCode = info.statusCode
|
err.statusCode = info.statusCode
|
||||||
err.response = {
|
err.response = {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import bodyParser from 'body-parser'
|
import bodyParser from 'body-parser'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import request from 'supertest'
|
import request from 'supertest'
|
||||||
import {IRequest} from './jsonrpc'
|
import {Request} from './jsonrpc'
|
||||||
import {createClient} from './supertest'
|
import {createClient} from './supertest'
|
||||||
import {ensure} from './ensure'
|
import {ensure} from './ensure'
|
||||||
import {jsonrpc} from './express'
|
import {jsonrpc} from './express'
|
||||||
@ -10,11 +10,11 @@ import {WithContext} from './types'
|
|||||||
|
|
||||||
describe('jsonrpc', () => {
|
describe('jsonrpc', () => {
|
||||||
|
|
||||||
interface IContext {
|
interface Context {
|
||||||
userId: number
|
userId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IService {
|
interface Service {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
delay(): Promise<void>
|
delay(): Promise<void>
|
||||||
syncError(message: string): void
|
syncError(message: string): void
|
||||||
@ -25,11 +25,11 @@ describe('jsonrpc', () => {
|
|||||||
addWithContext2(a: number, b: number): Promise<number>
|
addWithContext2(a: number, b: number): Promise<number>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ensureLoggedIn = ensure<IContext>(c => !!c.userId)
|
const ensureLoggedIn = ensure<Context>(c => !!c.userId)
|
||||||
|
|
||||||
class Service implements WithContext<IService, IContext> {
|
class MyService implements WithContext<Service, Context> {
|
||||||
constructor(readonly time: number) {}
|
constructor(readonly time: number) {}
|
||||||
add(context: IContext, a: number, b: number) {
|
add(context: Context, a: number, b: number) {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
multiply(...numbers: number[]) {
|
multiply(...numbers: number[]) {
|
||||||
@ -40,13 +40,13 @@ describe('jsonrpc', () => {
|
|||||||
setTimeout(resolve, this.time)
|
setTimeout(resolve, this.time)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
syncError(context: IContext, message: string) {
|
syncError(context: Context, message: string) {
|
||||||
throw new Error(message)
|
throw new Error(message)
|
||||||
}
|
}
|
||||||
async asyncError(context: IContext, message: string) {
|
async asyncError(context: Context, message: string) {
|
||||||
throw new Error(message)
|
throw new Error(message)
|
||||||
}
|
}
|
||||||
async httpError(context: IContext, statusCode: number, message: string) {
|
async httpError(context: Context, statusCode: number, message: string) {
|
||||||
const err: any = new Error(message)
|
const err: any = new Error(message)
|
||||||
err.statusCode = statusCode
|
err.statusCode = statusCode
|
||||||
err.errors = [{
|
err.errors = [{
|
||||||
@ -54,14 +54,14 @@ describe('jsonrpc', () => {
|
|||||||
}]
|
}]
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
addWithContext = (ctx: IContext, a: number, b: number) => {
|
addWithContext = (ctx: Context, a: number, b: number) => {
|
||||||
return a + b + ctx.userId
|
return a + b + ctx.userId
|
||||||
}
|
}
|
||||||
_private = () => {
|
_private = () => {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@ensureLoggedIn
|
@ensureLoggedIn
|
||||||
addWithContext2(ctx: IContext, a: number, b: number) {
|
addWithContext2(ctx: Context, a: number, b: number) {
|
||||||
return Promise.resolve(a + b + ctx!.userId)
|
return Promise.resolve(a + b + ctx!.userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,8 +72,8 @@ describe('jsonrpc', () => {
|
|||||||
const app = express()
|
const app = express()
|
||||||
app.use(bodyParser.json())
|
app.use(bodyParser.json())
|
||||||
app.use('/',
|
app.use('/',
|
||||||
jsonrpc(req => ({userId}), noopLogger)
|
jsonrpc(() => ({userId}), noopLogger)
|
||||||
.addService('/myService', new Service(5), [
|
.addService('/myService', new MyService(5), [
|
||||||
'add',
|
'add',
|
||||||
'delay',
|
'delay',
|
||||||
'syncError',
|
'syncError',
|
||||||
@ -87,7 +87,7 @@ describe('jsonrpc', () => {
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = createClient<IService>(createApp(), '/myService')
|
const client = createClient<Service>(createApp(), '/myService')
|
||||||
|
|
||||||
async function getError(promise: Promise<unknown>) {
|
async function getError(promise: Promise<unknown>) {
|
||||||
let error
|
let error
|
||||||
@ -260,7 +260,7 @@ describe('jsonrpc', () => {
|
|||||||
|
|
||||||
describe('hook', () => {
|
describe('hook', () => {
|
||||||
|
|
||||||
let requests: IRequest[] = []
|
let requests: Request[] = []
|
||||||
let results: any[] = []
|
let results: any[] = []
|
||||||
function create() {
|
function create() {
|
||||||
requests = []
|
requests = []
|
||||||
@ -268,12 +268,12 @@ describe('jsonrpc', () => {
|
|||||||
|
|
||||||
userId = 1000
|
userId = 1000
|
||||||
const app = express()
|
const app = express()
|
||||||
const myService = new Service(5)
|
const myService = new MyService(5)
|
||||||
// console.log('service', myService, Object.
|
// console.log('service', myService, Object.
|
||||||
app.use(bodyParser.json())
|
app.use(bodyParser.json())
|
||||||
app.use('/',
|
app.use('/',
|
||||||
jsonrpc(
|
jsonrpc(
|
||||||
req => Promise.resolve({userId}),
|
() => Promise.resolve({userId}),
|
||||||
noopLogger,
|
noopLogger,
|
||||||
async (details, makeRequest) => {
|
async (details, makeRequest) => {
|
||||||
requests.push(details.request)
|
requests.push(details.request)
|
||||||
|
|||||||
@ -1,35 +1,29 @@
|
|||||||
import express, {ErrorRequestHandler} from 'express'
|
import { Logger } from '@rondo.dev/logger'
|
||||||
import {FunctionPropertyNames} from './types'
|
import express, { ErrorRequestHandler, Request, Response, Router } from 'express'
|
||||||
|
import { createError, ErrorResponse, isRPCError } from './error'
|
||||||
import { IDEMPOTENT_METHOD_REGEX } from './idempotent'
|
import { IDEMPOTENT_METHOD_REGEX } from './idempotent'
|
||||||
import {IErrorResponse} from './error'
|
import { createRpcService, ERROR_METHOD_NOT_FOUND, ERROR_SERVER, IRequest, SuccessResponse } from './jsonrpc'
|
||||||
import {ILogger} from '@rondo.dev/logger'
|
import { FunctionPropertyNames } from './types'
|
||||||
import {ISuccessResponse} from './jsonrpc'
|
|
||||||
import {NextFunction, Request, Response, Router} from 'express'
|
|
||||||
import {createError, isJSONRPCError, IJSONRPCError, IError} from './error'
|
|
||||||
import {
|
|
||||||
createRpcService, ERROR_SERVER, ERROR_INVALID_PARAMS, ERROR_METHOD_NOT_FOUND,
|
|
||||||
IRequest,
|
|
||||||
} from './jsonrpc'
|
|
||||||
|
|
||||||
export type TGetContext<Context> = (req: Request) => Promise<Context> | Context
|
export type TGetContext<Context> = (req: Request) => Promise<Context> | Context
|
||||||
|
|
||||||
export interface IJSONRPCReturnType {
|
export interface RPCReturnType {
|
||||||
addService<T, F extends FunctionPropertyNames<T>>(
|
addService<T, F extends FunctionPropertyNames<T>>(
|
||||||
path: string,
|
path: string,
|
||||||
service: T,
|
service: T,
|
||||||
methods?: F[],
|
methods?: F[],
|
||||||
): IJSONRPCReturnType,
|
): RPCReturnType
|
||||||
router(): Router
|
router(): Router
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IInvocationDetails<A extends IRequest, Context> {
|
export interface InvocationDetails<A extends IRequest, Context> {
|
||||||
context: Context
|
context: Context
|
||||||
path: string
|
path: string
|
||||||
request: A
|
request: A
|
||||||
}
|
}
|
||||||
|
|
||||||
async function defaultHook<A extends IRequest, R, Context>(
|
async function defaultHook<A extends IRequest, R, Context>(
|
||||||
details: IInvocationDetails<A, Context>,
|
details: InvocationDetails<A, Context>,
|
||||||
invoke: () => Promise<R>,
|
invoke: () => Promise<R>,
|
||||||
): Promise<R> {
|
): Promise<R> {
|
||||||
const result = await invoke()
|
const result = await invoke()
|
||||||
@ -38,17 +32,18 @@ async function defaultHook<A extends IRequest, R, Context>(
|
|||||||
|
|
||||||
export function jsonrpc<Context>(
|
export function jsonrpc<Context>(
|
||||||
getContext: TGetContext<Context>,
|
getContext: TGetContext<Context>,
|
||||||
logger: ILogger,
|
logger: Logger,
|
||||||
hook: <A extends IRequest, R>(
|
hook: <A extends IRequest, R>(
|
||||||
details: IInvocationDetails<A, Context>,
|
details: InvocationDetails<A, Context>,
|
||||||
invoke: (request?: A) => Promise<R>) => Promise<R> = defaultHook,
|
invoke: (request?: A) => Promise<R>) => Promise<R> = defaultHook,
|
||||||
idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX,
|
idempotentMethodRegex = IDEMPOTENT_METHOD_REGEX,
|
||||||
): IJSONRPCReturnType {
|
): RPCReturnType {
|
||||||
|
|
||||||
|
/* eslint @typescript-eslint/no-unused-vars: 0 */
|
||||||
const handleError: ErrorRequestHandler = (err, req, res, next) => {
|
const handleError: ErrorRequestHandler = (err, req, res, next) => {
|
||||||
logger.error('JSON-RPC Error: %s', err.stack)
|
logger.error('JSON-RPC Error: %s', err.stack)
|
||||||
|
|
||||||
if (isJSONRPCError(err)) {
|
if (isRPCError(err)) {
|
||||||
res.status(err.statusCode)
|
res.status(err.statusCode)
|
||||||
res.json(err.response)
|
res.json(err.response)
|
||||||
return
|
return
|
||||||
@ -56,7 +51,7 @@ export function jsonrpc<Context>(
|
|||||||
|
|
||||||
const id = getRequestId(req)
|
const id = getRequestId(req)
|
||||||
const statusCode: number = err.statusCode || 500
|
const statusCode: number = err.statusCode || 500
|
||||||
const errorResponse: IErrorResponse<unknown> = {
|
const errorResponse: ErrorResponse<unknown> = {
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
result: null,
|
result: null,
|
||||||
@ -85,7 +80,7 @@ export function jsonrpc<Context>(
|
|||||||
const rpcService = createRpcService(service, methods)
|
const rpcService = createRpcService(service, methods)
|
||||||
|
|
||||||
function handleResponse(
|
function handleResponse(
|
||||||
response: ISuccessResponse<unknown> | null,
|
response: SuccessResponse<unknown> | null,
|
||||||
res: Response,
|
res: Response,
|
||||||
) {
|
) {
|
||||||
if (response === null) {
|
if (response === null) {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export type TId = number | string
|
export type TId = number | string
|
||||||
import {ArgumentTypes, FunctionPropertyNames, RetType} from './types'
|
import {ArgumentTypes, FunctionPropertyNames, RetType} from './types'
|
||||||
import {isPromise} from './isPromise'
|
import {isPromise} from './isPromise'
|
||||||
import {createError, IErrorResponse, IErrorWithData} from './error'
|
import {createError, ErrorResponse} from './error'
|
||||||
import {getValidatorsForMethod, getValidatorsForInstance} from './ensure'
|
import {getValidatorsForMethod, getValidatorsForInstance} from './ensure'
|
||||||
import {Validate} from './ensure'
|
import {Validate} from './ensure'
|
||||||
|
|
||||||
@ -35,8 +35,8 @@ export const ERROR_SERVER = {
|
|||||||
message: 'Server error',
|
message: 'Server error',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pick<T, K extends FunctionPropertyNames<T>>(t: T, keys: K[])
|
export function pick<T, K extends FunctionPropertyNames<T>>(
|
||||||
: Pick<T, K> {
|
t: T, keys: K[]): Pick<T, K> {
|
||||||
return keys.reduce((obj, key) => {
|
return keys.reduce((obj, key) => {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
const fn = t[key] as unknown as Function
|
const fn = t[key] as unknown as Function
|
||||||
@ -48,8 +48,8 @@ export function pick<T, K extends FunctionPropertyNames<T>>(t: T, keys: K[])
|
|||||||
export function getAllMethods<T>(obj: T): Array<FunctionPropertyNames<T>> {
|
export function getAllMethods<T>(obj: T): Array<FunctionPropertyNames<T>> {
|
||||||
const props = new Set<string>()
|
const props = new Set<string>()
|
||||||
do {
|
do {
|
||||||
const l = Object.getOwnPropertyNames(obj)
|
Object.getOwnPropertyNames(obj)
|
||||||
.filter((p, i, arr) => {
|
.filter(p => {
|
||||||
return typeof (obj as any)[p] === 'function' &&
|
return typeof (obj as any)[p] === 'function' &&
|
||||||
p.startsWith('_') === false &&
|
p.startsWith('_') === false &&
|
||||||
p !== 'constructor'
|
p !== 'constructor'
|
||||||
@ -61,24 +61,25 @@ export function getAllMethods<T>(obj: T): Array<FunctionPropertyNames<T>> {
|
|||||||
return Array.from(props) as unknown as Array<FunctionPropertyNames<T>>
|
return Array.from(props) as unknown as Array<FunctionPropertyNames<T>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRequest<M extends string = any, A = any[]> {
|
export interface Request<M extends string = any, A = any[]> {
|
||||||
jsonrpc: '2.0'
|
jsonrpc: '2.0'
|
||||||
id: TId | null
|
id: TId | null
|
||||||
method: M
|
method: M
|
||||||
params: A
|
params: A
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISuccessResponse<T> {
|
export interface SuccessResponse<T> {
|
||||||
jsonrpc: '2.0'
|
jsonrpc: '2.0'
|
||||||
id: TId
|
id: TId
|
||||||
result: T
|
result: T
|
||||||
error: null
|
error: null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IResponse<T = any> = ISuccessResponse<T> | IErrorResponse<T>
|
export type Response<T = any> = SuccessResponse<T> | ErrorResponse<T>
|
||||||
|
|
||||||
export function createSuccessResponse<T>(id: number | string, result: T)
|
export function createSuccessResponse<T>(
|
||||||
: ISuccessResponse<T> {
|
id: number | string, result: T
|
||||||
|
): SuccessResponse<T> {
|
||||||
return {
|
return {
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
id,
|
id,
|
||||||
@ -125,9 +126,9 @@ export const createRpcService = <T, M extends FunctionPropertyNames<T>>(
|
|||||||
pick(service, getAllMethods(service))
|
pick(service, getAllMethods(service))
|
||||||
return {
|
return {
|
||||||
async invoke<Context>(
|
async invoke<Context>(
|
||||||
req: IRequest<M, ArgumentTypes<T[M]>>,
|
req: Request<M, ArgumentTypes<T[M]>>,
|
||||||
context: Context,
|
context: Context,
|
||||||
): Promise<ISuccessResponse<RetType<T[M]>> | null> {
|
): Promise<SuccessResponse<RetType<T[M]>> | null> {
|
||||||
const {id, method, params} = req
|
const {id, method, params} = req
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -145,7 +146,7 @@ export const createRpcService = <T, M extends FunctionPropertyNames<T>>(
|
|||||||
const isNotification = req.id === null || req.id === undefined
|
const isNotification = req.id === null || req.id === undefined
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!rpcService.hasOwnProperty(method) ||
|
!Object.prototype.hasOwnProperty.call(rpcService, method) ||
|
||||||
typeof rpcService[method] !== 'function'
|
typeof rpcService[method] !== 'function'
|
||||||
) {
|
) {
|
||||||
throw createError(ERROR_METHOD_NOT_FOUND, {
|
throw createError(ERROR_METHOD_NOT_FOUND, {
|
||||||
|
|||||||
@ -1,29 +1,27 @@
|
|||||||
import { createLocalClient } from './local'
|
import { createLocalClient } from './local'
|
||||||
import {keys} from 'ts-transformer-keys'
|
import { WithContext } from './types'
|
||||||
import {WithContext, WithoutContext, RPCClient} from './types'
|
|
||||||
|
|
||||||
describe('local', () => {
|
describe('local', () => {
|
||||||
|
|
||||||
interface IService {
|
interface Service {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
addWithContext(a: number, b: number): number
|
addWithContext(a: number, b: number): number
|
||||||
}
|
}
|
||||||
const IServiceKeys = keys<IService>()
|
|
||||||
|
|
||||||
interface IContext {
|
interface Context {
|
||||||
userId: 1000
|
userId: 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
class Service implements WithContext<IService, IContext> {
|
class MyService implements WithContext<Service, Context> {
|
||||||
add(cx: IContext, a: number, b: number) {
|
add(cx: Context, a: number, b: number) {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
addWithContext = (cx: IContext, a: number, b: number) => {
|
addWithContext = (cx: Context, a: number, b: number) => {
|
||||||
return a + b + cx.userId
|
return a + b + cx.userId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = new Service()
|
const service = new MyService()
|
||||||
|
|
||||||
const proxy = createLocalClient(service, {userId: 1000})
|
const proxy = createLocalClient(service, {userId: 1000})
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import {RPCClient, WithContext, WithoutContext} from './types'
|
|
||||||
import {Request} from 'express'
|
|
||||||
import {TGetContext} from './express'
|
|
||||||
import { getAllMethods } from './jsonrpc'
|
import { getAllMethods } from './jsonrpc'
|
||||||
|
import { RPCClient, WithoutContext } from './types'
|
||||||
|
|
||||||
export type LocalClient<T> = RPCClient<WithoutContext<T>>
|
export type LocalClient<T> = RPCClient<WithoutContext<T>>
|
||||||
|
|
||||||
|
|||||||
@ -2,22 +2,22 @@
|
|||||||
* @jest-environment node
|
* @jest-environment node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createStore } from '@rondo.dev/redux'
|
||||||
import bodyParser from 'body-parser'
|
import bodyParser from 'body-parser'
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import {AddressInfo} from 'net'
|
|
||||||
import { Server } from 'http'
|
import { Server } from 'http'
|
||||||
import {WithContext, TPendingActions, TAllActions} from './types'
|
import { AddressInfo } from 'net'
|
||||||
import { combineReducers } from 'redux'
|
import { combineReducers } from 'redux'
|
||||||
|
import { keys } from 'ts-transformer-keys'
|
||||||
|
import { jsonrpc } from './express'
|
||||||
import { createActions, createReducer } from './redux'
|
import { createActions, createReducer } from './redux'
|
||||||
import { createRemoteClient } from './remote'
|
import { createRemoteClient } from './remote'
|
||||||
import {createStore} from '@rondo.dev/redux'
|
|
||||||
import {jsonrpc} from './express'
|
|
||||||
import {keys} from 'ts-transformer-keys'
|
|
||||||
import { noopLogger } from './test-utils'
|
import { noopLogger } from './test-utils'
|
||||||
|
import { WithContext } from './types'
|
||||||
|
|
||||||
describe('createActions', () => {
|
describe('createActions', () => {
|
||||||
|
|
||||||
interface IService {
|
interface Service {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
addAsync(a: number, b: number): Promise<number>
|
addAsync(a: number, b: number): Promise<number>
|
||||||
addStringsAsync(a: string, b: string): Promise<string>
|
addStringsAsync(a: string, b: string): Promise<string>
|
||||||
@ -26,25 +26,25 @@ describe('createActions', () => {
|
|||||||
throwError(bool: boolean): boolean
|
throwError(bool: boolean): boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IContext {
|
interface Context {
|
||||||
userId: number
|
userId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
class Service implements WithContext<IService, IContext> {
|
class MyService implements WithContext<Service, Context> {
|
||||||
add(cx: IContext, a: number, b: number) {
|
add(cx: Context, a: number, b: number) {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
addAsync(cx: IContext, a: number, b: number) {
|
addAsync(cx: Context, a: number, b: number) {
|
||||||
return new Promise<number>(resolve => resolve(a + b))
|
return new Promise<number>(resolve => resolve(a + b))
|
||||||
}
|
}
|
||||||
addStringsAsync(cx: IContext, a: string, b: string) {
|
addStringsAsync(cx: Context, a: string, b: string) {
|
||||||
return new Promise<string>(resolve => resolve(a + b))
|
return new Promise<string>(resolve => resolve(a + b))
|
||||||
}
|
}
|
||||||
addWithContext = (cx: IContext, a: number, b: number) =>
|
addWithContext = (cx: Context, a: number, b: number) =>
|
||||||
a + b + cx.userId
|
a + b + cx.userId
|
||||||
addAsyncWithContext = (cx: IContext, a: number, b: number) =>
|
addAsyncWithContext = (cx: Context, a: number, b: number) =>
|
||||||
new Promise<number>(resolve => resolve(a + b + cx.userId))
|
new Promise<number>(resolve => resolve(a + b + cx.userId))
|
||||||
throwError(cx: IContext, bool: boolean) {
|
throwError(cx: Context, bool: boolean) {
|
||||||
if (bool) {
|
if (bool) {
|
||||||
throw new Error('test')
|
throw new Error('test')
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ describe('createActions', () => {
|
|||||||
app.use(
|
app.use(
|
||||||
'/',
|
'/',
|
||||||
jsonrpc(() => ({userId: 1000}), noopLogger)
|
jsonrpc(() => ({userId: 1000}), noopLogger)
|
||||||
.addService('/service', new Service(), keys<IService>())
|
.addService('/service', new MyService(), keys<Service>())
|
||||||
.router(),
|
.router(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,8 +78,8 @@ describe('createActions', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function getClient() {
|
function getClient() {
|
||||||
const remoteClient = createRemoteClient<IService>(
|
const remoteClient = createRemoteClient<Service>(
|
||||||
baseUrl + '/service', keys<IService>())
|
baseUrl + '/service', keys<Service>())
|
||||||
const client = createActions(remoteClient, 'myService')
|
const client = createActions(remoteClient, 'myService')
|
||||||
|
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
@ -96,16 +96,14 @@ describe('createActions', () => {
|
|||||||
case 'addAsync':
|
case 'addAsync':
|
||||||
case 'addWithContext':
|
case 'addWithContext':
|
||||||
case 'addAsyncWithContext':
|
case 'addAsyncWithContext':
|
||||||
const r1: number = action.payload
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
add: r1,
|
add: action.payload,
|
||||||
}
|
}
|
||||||
case 'addStringsAsync':
|
case 'addStringsAsync':
|
||||||
const r2: string = action.payload
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
addStringsAsync: r2,
|
addStringsAsync: action.payload,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return state
|
return state
|
||||||
@ -139,7 +137,7 @@ describe('createActions', () => {
|
|||||||
add: action.payload,
|
add: action.payload,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
throwError(state, action) {
|
throwError(state) {
|
||||||
return state
|
return state
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
IRPCActions,
|
|
||||||
RPCClient,
|
|
||||||
RPCActions,
|
RPCActions,
|
||||||
|
RPCClient,
|
||||||
TResolvedActions,
|
TResolvedActions,
|
||||||
TAllActions,
|
TAllActions,
|
||||||
RPCReduxHandlers,
|
RPCReduxHandlers,
|
||||||
|
RPCActionsRecord,
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export function createActions<T, ActionType extends string>(
|
export function createActions<T, ActionType extends string>(
|
||||||
@ -27,18 +27,19 @@ export function createActions<T, ActionType extends string>(
|
|||||||
return service as RPCActions<T, ActionType>
|
return service as RPCActions<T, ActionType>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IState {
|
export interface TestState {
|
||||||
loading: number
|
loading: number
|
||||||
error: string
|
error: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createReducer<ActionType extends string, State extends IState>(
|
export function createReducer<
|
||||||
|
ActionType extends string, State extends TestState>(
|
||||||
actionType: ActionType,
|
actionType: ActionType,
|
||||||
defaultState: State,
|
defaultState: State,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const self = {
|
const self = {
|
||||||
withHandler<R extends IRPCActions<ActionType>>(
|
withHandler<R extends RPCActionsRecord<ActionType>>(
|
||||||
handleAction: (state: State, action: TResolvedActions<R>) => State,
|
handleAction: (state: State, action: TResolvedActions<R>) => State,
|
||||||
): (state: State | undefined, action: TAllActions<R>) => State {
|
): (state: State | undefined, action: TAllActions<R>) => State {
|
||||||
return (state: State = defaultState, action: TAllActions<R>): State => {
|
return (state: State = defaultState, action: TAllActions<R>): State => {
|
||||||
@ -66,7 +67,7 @@ export function createReducer<ActionType extends string, State extends IState>(
|
|||||||
}, action)
|
}, action)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
withMapping<R extends IRPCActions<ActionType>>(
|
withMapping<R extends RPCActionsRecord<ActionType>>(
|
||||||
handlers: RPCReduxHandlers<R, State>,
|
handlers: RPCReduxHandlers<R, State>,
|
||||||
) {
|
) {
|
||||||
return self.withHandler<R>((state, action) => {
|
return self.withHandler<R>((state, action) => {
|
||||||
|
|||||||
@ -14,24 +14,25 @@ import {WithContext} from './types'
|
|||||||
|
|
||||||
describe('remote', () => {
|
describe('remote', () => {
|
||||||
|
|
||||||
interface IService {
|
interface Service {
|
||||||
add(a: number, b: number): number
|
add(a: number, b: number): number
|
||||||
fetchItem(obj1: {a: number}, obj2: {b: number})
|
fetchItem(
|
||||||
: Promise<{a: number, b: number}>
|
obj1: {a: number}, obj2: {b: number}): Promise<{a: number, b: number}>
|
||||||
}
|
}
|
||||||
const IServiceKeys = keys<IService>()
|
const IServiceKeys = keys<Service>()
|
||||||
|
|
||||||
class Service implements WithContext<IService, {}> {
|
class MyService implements WithContext<Service, {}> {
|
||||||
add(ctx: {}, a: number, b: number) {
|
add(ctx: {}, a: number, b: number) {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
async fetchItem(ctx: {}, obj1: {a: number}, obj2: {b: number})
|
async fetchItem(
|
||||||
: Promise<{a: number, b: number}> {
|
ctx: {}, obj1: {a: number}, obj2: {b: number}
|
||||||
|
): Promise<{a: number, b: number}> {
|
||||||
return Promise.resolve({...obj1, ...obj2})
|
return Promise.resolve({...obj1, ...obj2})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const service = new Service()
|
const service = new MyService()
|
||||||
|
|
||||||
function createApp() {
|
function createApp() {
|
||||||
const a = express()
|
const a = express()
|
||||||
@ -65,7 +66,7 @@ describe('remote', () => {
|
|||||||
|
|
||||||
describe('idempotent method invocation (GET)', () => {
|
describe('idempotent method invocation (GET)', () => {
|
||||||
it('creates a proxy for remote service', async () => {
|
it('creates a proxy for remote service', async () => {
|
||||||
const rpc = createRemoteClient<IService>(
|
const rpc = createRemoteClient<Service>(
|
||||||
baseUrl + '/myService', IServiceKeys)
|
baseUrl + '/myService', IServiceKeys)
|
||||||
const result = await rpc.fetchItem({a: 10}, {b: 20})
|
const result = await rpc.fetchItem({a: 10}, {b: 20})
|
||||||
expect(result).toEqual({a: 10, b: 20})
|
expect(result).toEqual({a: 10, b: 20})
|
||||||
@ -74,7 +75,7 @@ describe('remote', () => {
|
|||||||
|
|
||||||
describe('method invocation (POST)', () => {
|
describe('method invocation (POST)', () => {
|
||||||
it('creates a proxy for remote service', async () => {
|
it('creates a proxy for remote service', async () => {
|
||||||
const rpc = createRemoteClient<IService>(
|
const rpc = createRemoteClient<Service>(
|
||||||
baseUrl + '/myService', IServiceKeys)
|
baseUrl + '/myService', IServiceKeys)
|
||||||
const result = await rpc.add(3, 7)
|
const result = await rpc.add(3, 7)
|
||||||
expect(result).toBe(3 + 7)
|
expect(result).toBe(3 + 7)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export function createRemoteClient<T>(
|
|||||||
method: string,
|
method: string,
|
||||||
params: any[],
|
params: any[],
|
||||||
) {
|
) {
|
||||||
const reqMethod = IDEMPOTENT_METHOD_REGEX.test(method) ? 'GET' : 'POST'
|
const reqMethod = idempotentMethodRegex.test(method) ? 'GET' : 'POST'
|
||||||
const payloadKey = reqMethod === 'POST' ? 'data' : 'params'
|
const payloadKey = reqMethod === 'POST' ? 'data' : 'params'
|
||||||
|
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import {ILogger} from '@rondo.dev/logger'
|
import {Logger} from '@rondo.dev/logger'
|
||||||
|
|
||||||
const noop = () => undefined
|
const noop = () => undefined
|
||||||
|
|
||||||
export const noopLogger: ILogger = {
|
export const noopLogger: Logger = {
|
||||||
error: noop,
|
error: noop,
|
||||||
warn: noop,
|
warn: noop,
|
||||||
info: noop,
|
info: noop,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {IPendingAction, IResolvedAction, IRejectedAction} from '@rondo.dev/redux'
|
import {PendingAction, ResolvedAction, RejectedAction} from '@rondo.dev/redux'
|
||||||
|
|
||||||
export type ArgumentTypes<T> =
|
export type ArgumentTypes<T> =
|
||||||
T extends (...args: infer U) => infer R ? U : never
|
T extends (...args: infer U) => infer R ? U : never
|
||||||
@ -38,64 +38,64 @@ export type RPCClient<T> = {
|
|||||||
[K in keyof T]: PromisifyReturnType<T[K]>
|
[K in keyof T]: PromisifyReturnType<T[K]>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRPCActions<ActionType extends string> {
|
export interface RPCActionsRecord<ActionType extends string> {
|
||||||
[key: string]: (...a: any[]) => IRPCPendingAction<any, ActionType, typeof key>
|
[key: string]: (...a: any[]) => RPCPendingAction<any, ActionType, typeof key>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RPCActions<T, ActionType extends string> = {
|
export type RPCActions<T, ActionType extends string> = {
|
||||||
[K in keyof T]: (...a: ArgumentTypes<T[K]>) =>
|
[K in keyof T]: (...a: ArgumentTypes<T[K]>) =>
|
||||||
IRPCPendingAction<UnwrapPromise<RetType<T[K]>>, ActionType, K>
|
RPCPendingAction<UnwrapPromise<RetType<T[K]>>, ActionType, K>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RPCReduxHandlers<T, State> = {
|
export type RPCReduxHandlers<T, State> = {
|
||||||
[K in keyof T]: (
|
[K in keyof T]: (
|
||||||
state: State,
|
state: State,
|
||||||
action: TResolved<TPending<RetType<T[K]>>>,
|
action: GetResolvedAction<GetPendingAction<RetType<T[K]>>>,
|
||||||
) => Partial<State>
|
) => Partial<State>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRPCPendingAction<
|
export interface RPCPendingAction<
|
||||||
T, ActionType extends string, Method extends string | number | symbol
|
T, ActionType extends string, Method extends string | number | symbol
|
||||||
> extends IPendingAction<T, ActionType> {
|
> extends PendingAction<T, ActionType> {
|
||||||
method: Method
|
method: Method
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRPCResolvedAction<
|
export interface RPCResolvedAction<
|
||||||
T, ActionType extends string, Method extends string | symbol | number
|
T, ActionType extends string, Method extends string | symbol | number
|
||||||
> extends IResolvedAction<T, ActionType> {
|
> extends ResolvedAction<T, ActionType> {
|
||||||
method: Method
|
method: Method
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRPCRejectedAction<
|
export interface RPCRejectedAction<
|
||||||
ActionType extends string, Method extends string | symbol | number
|
ActionType extends string, Method extends string | symbol | number
|
||||||
> extends IRejectedAction<ActionType> {
|
> extends RejectedAction<ActionType> {
|
||||||
method: Method
|
method: Method
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TRPCAction<
|
export type TRPCAction<
|
||||||
T, ActionType extends string, Method extends string | number | symbol
|
T, ActionType extends string, Method extends string | number | symbol
|
||||||
> =
|
> =
|
||||||
IRPCPendingAction<T, ActionType, Method>
|
RPCPendingAction<T, ActionType, Method>
|
||||||
| IRPCResolvedAction<T, ActionType, Method>
|
| RPCResolvedAction<T, ActionType, Method>
|
||||||
| IRPCRejectedAction<ActionType, Method>
|
| RPCRejectedAction<ActionType, Method>
|
||||||
|
|
||||||
export type TResolved<A> =
|
export type GetResolvedAction<A> =
|
||||||
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
|
A extends RPCPendingAction<infer T, infer ActionType, infer Method>
|
||||||
? IRPCResolvedAction<T, ActionType, Method>
|
? RPCResolvedAction<T, ActionType, Method>
|
||||||
: never
|
: never
|
||||||
|
|
||||||
export type TRejected<A> =
|
export type GetRejectedAction<A> =
|
||||||
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
|
A extends RPCPendingAction<infer T, infer ActionType, infer Method>
|
||||||
? IRPCRejectedAction<ActionType, Method>
|
? RPCRejectedAction<ActionType, Method>
|
||||||
: never
|
: never
|
||||||
|
|
||||||
export type TPending<A> =
|
export type GetPendingAction<A> =
|
||||||
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
|
A extends RPCPendingAction<infer T, infer ActionType, infer Method>
|
||||||
? IRPCPendingAction<T, ActionType, Method>
|
? RPCPendingAction<T, ActionType, Method>
|
||||||
: never
|
: never
|
||||||
|
|
||||||
type Values<T> = T[keyof T]
|
type Values<T> = T[keyof T]
|
||||||
export type TPendingActions<T> = TPending<RetType<Values<T>>>
|
export type TPendingActions<T> = GetPendingAction<RetType<Values<T>>>
|
||||||
export type TResolvedActions<T> = TResolved<TPendingActions<T>>
|
export type TResolvedActions<T> = GetResolvedAction<TPendingActions<T>>
|
||||||
export type TAllActions<T> = TPendingActions<T>
|
export type TAllActions<T> = TPendingActions<T>
|
||||||
| TResolvedActions<T> | TRejected<TPendingActions<T>>
|
| TResolvedActions<T> | GetRejectedAction<TPendingActions<T>>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import {PendingAction} from './IPendingAction'
|
import {PendingAction} from './PendingAction'
|
||||||
|
|
||||||
export function createPendingAction<T, ActionType extends string>(
|
export function createPendingAction<T, ActionType extends string>(
|
||||||
payload: Promise<T>,
|
payload: Promise<T>,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user