업그레이드
- 이름 변경 ProcDecorator - Promise - Pre, Post 모드 분리
This commit is contained in:
+131
-28
@@ -1,44 +1,147 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
type MayBePromise<T> = T | Promise<T>;
|
||||||
import { ReqLogin } from "./reqLogin";
|
|
||||||
import { ReqGameLogin } from "./reqGameLogin";
|
export type InvalidProc = {
|
||||||
import { ParseUserLevel } from "./ParseUserLevel";
|
invalidProcSymbol: unknown;
|
||||||
import { DeclConverter } from "./base2";
|
|
||||||
export type InvalidConversion = {
|
|
||||||
isInvalid: true;
|
|
||||||
reason?: string;
|
reason?: string;
|
||||||
alreadyProcessed?: boolean;
|
alreadyProcessed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseConverter<In extends object, Out extends In,>{
|
export interface ProcDecorator<Out, In extends object = Record<string, never>> {
|
||||||
(inCtx: In, request: Request, response: Response): Out | InvalidConversion;
|
(inCtx: In, req: Request, res: Response): MayBePromise<Out | InvalidProc>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Chain<In>(args: In, request: Request, response: Response) {
|
type Empty = Record<string, never>;
|
||||||
return {
|
|
||||||
next: <Out extends In,>(inner: (payload: In, request: Request, response: Response) => Out) => {
|
type Login = {
|
||||||
return Chain(inner(args, request, response), request, response);
|
userID: number;
|
||||||
}
|
}
|
||||||
|
type GameLogin = {
|
||||||
|
userID: number;
|
||||||
|
generalID: number;
|
||||||
|
}
|
||||||
|
type UserLevel = {
|
||||||
|
userID: number;
|
||||||
|
userLevel: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ParseUserLevel<Q extends Login = Login>(): ProcDecorator<Q & UserLevel, Q> {
|
||||||
|
return (inCtx) => {
|
||||||
|
return {
|
||||||
|
userLevel: 123,
|
||||||
|
...inCtx,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtendsType<U, T> = T extends U ? T : never;
|
export function ReqLogin<Q extends object = Empty>(): [ProcDecorator<Q & Login, Q>, ProcDecorator<Login & object, Login & object>] {
|
||||||
|
return [
|
||||||
|
(inCtx) => {
|
||||||
|
return {
|
||||||
|
userID: 999,
|
||||||
|
...inCtx,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
(inCtx) => {
|
||||||
|
//무언가
|
||||||
|
return inCtx;
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReqGameLogin<Q extends Login = Login>(): ProcDecorator<Q & GameLogin, Q> {
|
||||||
|
return (inCtx) => {
|
||||||
|
return {
|
||||||
|
generalID: inCtx.userID * 4,
|
||||||
|
...inCtx,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Compose2<B extends object, A extends object, D, C> = D & B extends A & C ? B extends C ? ProcDecorator<D & B, A> : never : never;
|
||||||
|
|
||||||
|
type PD1<B extends object, A extends object> = () => ProcDecorator<B, A>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
type PD2<B extends object, A extends object> = () => [ProcDecorator<B, A>, ProcDecorator<any, any>];
|
||||||
|
|
||||||
|
type PackChain<T> =
|
||||||
|
T extends readonly [] ? ProcDecorator<Empty, Empty> :
|
||||||
|
T extends readonly [PD1<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||||
|
T extends readonly [PD2<infer B, infer A>] ? ProcDecorator<B, A> :
|
||||||
|
T extends readonly [PD1<infer B, infer A>, PD1<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||||
|
T extends readonly [PD2<infer B, infer A>, PD1<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||||
|
T extends readonly [PD1<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||||
|
T extends readonly [PD2<infer B, infer A>, PD2<infer D, infer C>, ... infer R] ? PackChain<[() => Compose2<B, A, D, C>, ...R]> :
|
||||||
|
never;
|
||||||
|
|
||||||
|
type Resolve<T> = T extends ProcDecorator<infer B, infer A> ? Empty extends A ? B : never : never;
|
||||||
|
|
||||||
|
|
||||||
|
export type ResolveChain<T extends ProcDecoratorChain> = Resolve<PackChain<T>>;
|
||||||
|
|
||||||
|
const q1 = [
|
||||||
|
ReqLogin,
|
||||||
|
ParseUserLevel,
|
||||||
|
ReqGameLogin,
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const q2 = [
|
||||||
|
ReqLogin,
|
||||||
|
ParseUserLevel,
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
|
||||||
|
type R1 = Resolve<PackChain<typeof q1>>;
|
||||||
|
type R2 = Resolve<PackChain<typeof q2>>;
|
||||||
|
const r1: R1 = undefined as unknown as R1;
|
||||||
|
const r2: R2 = undefined as unknown as R2;
|
||||||
|
r1.userLevel;
|
||||||
|
r2.userID;
|
||||||
|
|
||||||
|
|
||||||
|
async function test(req: Request, res: Response) {
|
||||||
|
const a0 = await ReqLogin()[0]({}, req, res);
|
||||||
|
if ('invalidProcSymbol' in a0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const a1 = await ParseUserLevel()(a0, req, res);
|
||||||
|
if ('invalidProcSymbol' in a1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const a2 = await ReqGameLogin()(a1, req, res);
|
||||||
|
if ('invalidProcSymbol' in a2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const b0 = await ReqLogin()[1](a2, req, res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export type ProcDecoratorChain = readonly ((() => ProcDecorator<any, any>) | (() => [ProcDecorator<any, any>, ProcDecorator<any, any>]))[];
|
||||||
|
|
||||||
|
function unpack2<A extends ProcDecorator<any, any>, B>(v: A | [A, B]): A {
|
||||||
|
if (Array.isArray(v)) {
|
||||||
|
return v[0];
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
//export function DeclConverter(): [[], Record<string, unknown>];
|
function test2<T extends ProcDecoratorChain>(req: Request, res: Response, chain: T): Resolve<PackChain<T>> | false {
|
||||||
|
const tmp = chain[0]();
|
||||||
|
|
||||||
|
const actors = chain.map((v) => unpack2(v()));
|
||||||
|
|
||||||
//TODO: ReqLogin이 위에 있을 경우에만 통과시켜야..?
|
let result = {};
|
||||||
const a = DeclConverter(
|
for (const actor of actors) {
|
||||||
ParseUserLevel(),
|
result = actor(result, req, res);
|
||||||
ReqLogin(),
|
if ('isInvalid' in result) {
|
||||||
ReqGameLogin(),
|
return false;
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
return result as Resolve<PackChain<T>>;
|
||||||
|
}
|
||||||
|
|
||||||
type b = Exclude<(typeof a)[1], InvalidConversion>;
|
const finalResult1 = test2({} as Request, {} as Response, q1);
|
||||||
const c: b = undefined as unknown as b;
|
if (finalResult1 !== false) {
|
||||||
//흠....
|
finalResult1.userLevel;
|
||||||
console.log(c.generalID);
|
}
|
||||||
console.log(c.userID);
|
|
||||||
console.log(c.userLevel);
|
|
||||||
Vendored
-17
@@ -1,17 +0,0 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
type C = (...args: any) => any;
|
|
||||||
type Rev<A> = ()=> A;
|
|
||||||
|
|
||||||
type ChainResult<T> =
|
|
||||||
T extends [infer R0 extends C] ? ReturnType<R0> :
|
|
||||||
T extends [infer R1 extends C, infer R0 extends C] ? ReturnType<R1> & ReturnType<R0> :
|
|
||||||
T extends [infer R2, infer R1, ...infer RX] ? ChainResult2<R2, R1, ChainResult<[R2, R1]>, ChainResult<[Rev<ChainResult<[R2, R1]>>, ...RX]>> :
|
|
||||||
never;
|
|
||||||
|
|
||||||
type ChainResult2<A, B, T, ANS> = T extends ChainResult<[A, B]> ? ANS: never;
|
|
||||||
|
|
||||||
export function DeclConverter<R0 extends C>(r0: R0): [[R0], ReturnType<R0>];
|
|
||||||
export function DeclConverter<R0, R1>(r0: R0, r1: R1): [[R0, R1], ChainResult<[R0, R1]>];
|
|
||||||
export function DeclConverter<R0, R1, R2>(r0: R0, r1: R1, r2:R2) : [[R0, R1, R2], ChainResult<[R0, R1, R2]>];
|
|
||||||
export function DeclConverter<R0, R1, R2, R3>(r0: R0, r1: R1, r2:R2, r3:R3) : [[R0, R1, R2, R3], ChainResult<[R0, R1, R2, R3]>];
|
|
||||||
export function DeclConverter<R0, R1, R2, R3, R4>(r0: R0, r1: R1, r2:R2, r3:R3, r4:R4) : [[R0, R1, R2, R3, R4], ChainResult<[R0, R1, R2, R3, R4]>];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export function DeclConverter(...target){
|
|
||||||
return [target, undefined];
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import { InvalidConversion } from "./base";
|
|
||||||
|
|
||||||
type MayBePromise<T> = T | Promise<T>;
|
|
||||||
|
|
||||||
export interface Converter<Out, In extends object = Record<string, never>> {
|
|
||||||
(inCtx: In, req: Request, res: Response): MayBePromise<Out | InvalidConversion>;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Empty = Record<string, never>;
|
|
||||||
|
|
||||||
type Login = {
|
|
||||||
userID: number;
|
|
||||||
}
|
|
||||||
type GameLogin = {
|
|
||||||
userID: number;
|
|
||||||
generalID: number;
|
|
||||||
}
|
|
||||||
type UserLevel = {
|
|
||||||
userID: number;
|
|
||||||
userLevel: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ParseUserLevel<Q extends Login = Login>(): Converter<Q & UserLevel, Q> {
|
|
||||||
return (inCtx) => {
|
|
||||||
return {
|
|
||||||
userLevel: 123,
|
|
||||||
...inCtx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ReqLogin<Q extends object = Empty>(): Converter<Q & Login, Q> {
|
|
||||||
return (inCtx) => {
|
|
||||||
return {
|
|
||||||
userID: 999,
|
|
||||||
...inCtx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ReqGameLogin<Q extends Login = Login>(): Converter<Q & GameLogin, Q> {
|
|
||||||
return (inCtx) => {
|
|
||||||
return {
|
|
||||||
generalID: inCtx.userID * 4,
|
|
||||||
...inCtx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Compose2<B, A extends object, D, C> = D & B extends A & C ? B extends C ? Converter<D & B, A> : never : never;
|
|
||||||
export type Chain<T> =
|
|
||||||
T extends readonly [() => Converter<infer B, infer A>] ? Converter<B, A> :
|
|
||||||
T extends readonly [() => Converter<infer B, infer A>, () => Converter<infer D, infer C>] ? Compose2<B, A, D, C> :
|
|
||||||
T extends readonly [() => Converter<infer B, infer A>, () => Converter<infer D, infer C>, ... infer R] ? Chain<[() => Compose2<B, A, D, C>, ...R]> :
|
|
||||||
never;
|
|
||||||
|
|
||||||
|
|
||||||
export type Solve<T> = T extends Converter<infer B, infer A> ? Empty extends A ? B : never : never;
|
|
||||||
|
|
||||||
|
|
||||||
const q1 = [
|
|
||||||
ReqLogin,
|
|
||||||
ParseUserLevel,
|
|
||||||
ReqGameLogin,
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
const q2 = [
|
|
||||||
ReqLogin,
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
type R1 = Solve<Chain<typeof q1>>;
|
|
||||||
type R2 = Solve<Chain<typeof q2>>;
|
|
||||||
const r1: R1 = undefined as unknown as R1;
|
|
||||||
const r2: R2 = undefined as unknown as R2;
|
|
||||||
r1.userLevel;
|
|
||||||
r2.userID;
|
|
||||||
|
|
||||||
|
|
||||||
async function test(req: Request, res: Response){
|
|
||||||
const a0 = await ReqLogin()({}, req, res);
|
|
||||||
if('isInvalid' in a0){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const a1 = await ParseUserLevel()(a0, req, res);
|
|
||||||
if('isInvalid' in a1){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const a2 = await ReqGameLogin()(a1, req, res);
|
|
||||||
if('isInvalid' in a2){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export type Converters = readonly (() => Converter<any, any>)[];
|
|
||||||
|
|
||||||
function test2<T extends Converters>(req: Request, res: Response, chain: T): Solve<Chain<T>> | false{
|
|
||||||
|
|
||||||
const actors = chain.map((v) => v());
|
|
||||||
|
|
||||||
let result = {};
|
|
||||||
for(const actor of actors){
|
|
||||||
result = actor(result, req, res);
|
|
||||||
if('isInvalid' in result){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result as Solve<Chain<T>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const finalResult1 = test2({} as Request, {} as Response, q1);
|
|
||||||
if(finalResult1 !== false){
|
|
||||||
finalResult1.userLevel;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user