refac: api 시스템 교체
- procDecorator의 타입 복잡도를 낮춤 - 타입 에러가 살짝 더 깔끔함
This commit is contained in:
@@ -9,12 +9,14 @@ export interface UserLevelCtx {
|
||||
}
|
||||
|
||||
export function ParseUserLevel<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGenerator<UserLevelCtx, Q> {
|
||||
return (inCtx) => {
|
||||
return (ctx) => {
|
||||
const userLevel = 123;
|
||||
inCtx.setValue('userLevel', userLevel);
|
||||
return {
|
||||
ctx.setValue('userLevel', userLevel);
|
||||
return [{
|
||||
result: true,
|
||||
}, {
|
||||
userLevel,
|
||||
...inCtx,
|
||||
}
|
||||
...ctx,
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -12,10 +12,14 @@ export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGe
|
||||
return async (inCtx) => {
|
||||
const ctx: Q & Partial<GameLoginCtx> = inCtx;
|
||||
if (ctx.generalID !== undefined) {
|
||||
return {
|
||||
return [{
|
||||
result: true,
|
||||
type: 'GameLogin',
|
||||
info: 'UseSession',
|
||||
}, {
|
||||
generalID: ctx.generalID,
|
||||
...inCtx,
|
||||
};
|
||||
}, ];
|
||||
}
|
||||
|
||||
console.log('Something GameLogin');
|
||||
@@ -23,9 +27,13 @@ export function ReqGameLogin<Q extends LoginCtx & SessionCtx>(): ProcDecoratorGe
|
||||
|
||||
inCtx.setValue('generalID', ctx.userID * 4);
|
||||
|
||||
return {
|
||||
return [{
|
||||
result: true,
|
||||
type: 'GameLogin',
|
||||
info: 'UseDB',
|
||||
}, {
|
||||
generalID: ctx.userID * 4,
|
||||
...inCtx,
|
||||
}
|
||||
}, ]
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
import type { SessionCtx } from "./StartSession";
|
||||
import type { InvalidProc, ProcDecoratorGenerator } from "./base";
|
||||
import type { DecoratorResultFalse, DecoratorResultTrue, ProcDecorator, ProcDecoratorGenerator } from "./base";
|
||||
|
||||
export type LoginCtx = {
|
||||
userID: number;
|
||||
}
|
||||
|
||||
export function ReqLogin<Q extends SessionCtx>(): ProcDecoratorGenerator<LoginCtx, Q> {
|
||||
return (inCtx) => {
|
||||
const ctx: Q & Partial<LoginCtx> = inCtx;
|
||||
export function ReqLogin<Q extends SessionCtx>(): ProcDecorator<LoginCtx & Q, Q> {
|
||||
return (ctx) => {
|
||||
const userID = ctx.userID;
|
||||
|
||||
if (userID === undefined) {
|
||||
return {
|
||||
return [{
|
||||
result: false,
|
||||
reason: 'Required Login',
|
||||
invalidProcSymbol: 'ReqLogin'
|
||||
} satisfies InvalidProc;
|
||||
type: 'Required Login',
|
||||
info: 'ReqLogin'
|
||||
}, ctx];
|
||||
}
|
||||
|
||||
return ctx as LoginCtx & Q;
|
||||
return [{ result: true }, ctx as Q & LoginCtx];
|
||||
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export type SessionCtx = {
|
||||
export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator<SessionCtx, Q> {
|
||||
return (inCtx) => {
|
||||
const raw: Record<string, unknown> = {};
|
||||
return {
|
||||
return [{ result: true }, {
|
||||
clearSession: () => Promise.resolve(),
|
||||
deleteValue: async (key: string) => {
|
||||
console.log('deleteValue', key);
|
||||
@@ -34,6 +34,6 @@ export function StartSession<Q extends object = Empty>(): ProcDecoratorGenerator
|
||||
changed: false,
|
||||
},
|
||||
...inCtx,
|
||||
};
|
||||
}];
|
||||
}
|
||||
}
|
||||
@@ -1,131 +1,153 @@
|
||||
import type { Request, Response } from "express";
|
||||
import type { InvalidResponse } from "../../apiStructure/defs";
|
||||
|
||||
type MayBePromise<T> = T | Promise<T>;
|
||||
|
||||
export type Empty = Record<string, never>;
|
||||
export type DecoratorStack = {
|
||||
result: boolean,
|
||||
type: string,
|
||||
info: string,
|
||||
}[];
|
||||
|
||||
export type DecoratorResultTrue = {
|
||||
result: true;
|
||||
type?: string;
|
||||
info?: string;
|
||||
};
|
||||
export type DecoratorResultFalse = {
|
||||
result: false;
|
||||
type: string;
|
||||
info: string;
|
||||
}
|
||||
export type DecoratorResult = DecoratorResultTrue | DecoratorResultFalse;
|
||||
export type DecoratorStack = DecoratorResult[];
|
||||
|
||||
export interface ProcDecorator<Out extends object, In extends object = Record<string, never>> {
|
||||
(inCtx: In & Partial<Out>, req: Request, res: Response): MayBePromise<[Out, DecoratorStack]>;
|
||||
(inCtx: In & Partial<Out>, req: Request, res: Response)
|
||||
: MayBePromise<[DecoratorResultTrue, Out]>
|
||||
| MayBePromise<[DecoratorResultFalse, In & Partial<Out>]>;
|
||||
}
|
||||
|
||||
export interface PostProcDecorator<T extends object>{
|
||||
(ctx: T, stack: DecoratorStack, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[T, DecoratorStack]>;
|
||||
export interface PostProcDecorator<T extends object> {
|
||||
(ctx: T, preResult: DecoratorResult, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorResult, T]>;
|
||||
}
|
||||
|
||||
export interface ProcDecoratorRunner<Out extends object, In extends object> {
|
||||
(inCtx: In & Partial<Out>, req: Request, res: Response): MayBePromise<[DecoratorStack, Out]>;
|
||||
}
|
||||
|
||||
export interface PostProcDecoratorRunner<T extends object> {
|
||||
(ctx: T, preResult: DecoratorStack, req: Request, res: Response, isValidRoute: boolean): MayBePromise<[DecoratorStack, T]>;
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type PlainDecorator = ProcDecorator<any, any>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type PlainPostDecorator = PostProcDecorator<any>;
|
||||
|
||||
|
||||
export type ProcDecoratorGenerator<B extends object, A extends object> = ProcDecorator<B & A, A>;
|
||||
export type ProcDecoratorPrePostGenerator<B extends object, A extends object> = [ProcDecorator<B & A, A>, PostProcDecorator<B & A>];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ProcDecoratorChain = undefined | readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[];
|
||||
export type ProcDecoratorChain = readonly ((() => PlainDecorator) | (() => [PlainDecorator, PlainPostDecorator]))[];
|
||||
|
||||
export type ResolveChain<T> = T extends undefined ? Empty : T extends ProcDecoratorChain ? Resolve<PackChain<T>> : never;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
type ParseInType<T> = T extends ProcDecorator<infer B, infer A> ? A : never;
|
||||
type ParseOutType<T> = T extends ProcDecorator<infer B, infer A> ? B : never;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ParseInType<T> = T extends ProcDecorator<any, infer A> ? A : never;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type ParseOutType<T> = T extends ProcDecorator<infer B, any> ? B : never;
|
||||
|
||||
export function declProcDecorators<T extends ProcDecoratorChain>(decorators: T) {
|
||||
type InType = ParseInType<PackChain<T>>;
|
||||
type OutType = ParseOutType<PackChain<T>>;
|
||||
|
||||
const preDecorator: PlainDecorator[] = [];
|
||||
const postDecorator: (PlainDecorator | undefined)[] = [];
|
||||
const postDecorator: (PlainPostDecorator | undefined)[] = [];
|
||||
if (decorators) {
|
||||
for (const procGen of decorators) {
|
||||
const proc = procGen();
|
||||
if (Array.isArray(proc)) {
|
||||
preDecorator.push(proc[0]);
|
||||
postDecorator.push(undefined);
|
||||
postDecorator.push(proc[1]);
|
||||
}
|
||||
else {
|
||||
preDecorator.push(proc);
|
||||
postDecorator.push(proc);
|
||||
postDecorator.push(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const packedDecorators: readonly [ProcDecorator<OutType, InType>, PostProcDecorator<OutType>] = [
|
||||
const packedDecorators: readonly [ProcDecoratorRunner<OutType, InType>, PostProcDecoratorRunner<OutType>] = [
|
||||
async (ctx, req, res) => {
|
||||
let rctx = ctx as unknown as OutType;
|
||||
const decoratorStack: DecoratorStack = [];
|
||||
if (!preDecorator) {
|
||||
return [rctx, decoratorStack];
|
||||
}
|
||||
return [decoratorStack, rctx];
|
||||
}
|
||||
|
||||
for (const [idx, proc] of preDecorator.entries()) {
|
||||
try {
|
||||
const [newCtx, [stackResult]] = await proc(rctx, req, res);
|
||||
const [stackResult, newCtx] = await proc(rctx, req, res);
|
||||
decoratorStack.push(stackResult);
|
||||
|
||||
if(stackResult.result){
|
||||
if (stackResult.result) {
|
||||
rctx = newCtx;
|
||||
continue;
|
||||
}
|
||||
|
||||
return [newCtx, decoratorStack];
|
||||
return [decoratorStack, newCtx];
|
||||
}
|
||||
catch (e) {
|
||||
|
||||
return [{
|
||||
while (decoratorStack.length > idx) {
|
||||
decoratorStack.pop();
|
||||
}
|
||||
decoratorStack.push({
|
||||
result: false,
|
||||
reason: `internal error[${idx}]: ${e}`,
|
||||
invalidProcInfo: [['PreThrow', idx]],
|
||||
}, rctx];
|
||||
type: 'PreThrow',
|
||||
info: `internal error: ${e}`,
|
||||
});
|
||||
|
||||
return [decoratorStack, rctx];
|
||||
}
|
||||
}
|
||||
|
||||
return [rctx, decoratorStack];
|
||||
return [decoratorStack, rctx];
|
||||
},
|
||||
async (ctx, stack, req, res) => {
|
||||
if (!postDecorator) {
|
||||
return [stack, ctx];
|
||||
}
|
||||
const isValidRoute = stack.length === postDecorator.length && stack.every(v => v.result);
|
||||
while (stack.length > 0) {
|
||||
const preStackResult = stack.pop() as DecoratorResult;
|
||||
const idx = stack.length;
|
||||
const proc = postDecorator[idx];
|
||||
|
||||
return [ctx, stack];
|
||||
if (!proc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const [postStackResult, nextCtx] = await proc(ctx, preStackResult, req, res, isValidRoute);
|
||||
if (!postStackResult.result) {
|
||||
stack.push(postStackResult);
|
||||
return [stack, nextCtx];
|
||||
}
|
||||
|
||||
ctx = nextCtx;
|
||||
}
|
||||
catch (e) {
|
||||
stack.push({
|
||||
result: false,
|
||||
type: 'PostThrow',
|
||||
info: `internal error: ${e}`,
|
||||
});
|
||||
return [stack, ctx];
|
||||
}
|
||||
|
||||
}
|
||||
return [stack, ctx];
|
||||
},
|
||||
] as const;
|
||||
|
||||
return packedDecorators;
|
||||
|
||||
return [
|
||||
async (ctx: InType & Partial<OutType>, req: Request, res: Response) => {
|
||||
return ctx as unknown as OutType;
|
||||
/*if (!preDecorator) {
|
||||
return ctx as Result;
|
||||
}
|
||||
|
||||
for (const [idx, proc] of preDecorator.entries()) {
|
||||
try {
|
||||
const result = await proc(ctx, req, res, true) as Result | [InvalidProc, object];
|
||||
if (Array.isArray(result)) {
|
||||
const [invalidProc, erroredCtx] = result;
|
||||
invalidProc.invalidProcInfo.push(['Pre', idx]);
|
||||
throw [invalidProc, erroredCtx as Partial<Result>]
|
||||
return ;
|
||||
}
|
||||
ctx = result;
|
||||
}
|
||||
catch (e) {
|
||||
return [{
|
||||
result: false,
|
||||
reason: `internal error[${idx}]: ${e}`,
|
||||
invalidProcSymbol: ['preCtx', idx],
|
||||
}, ctx, idx];
|
||||
}
|
||||
}
|
||||
return ctx as ResolveChain<T>;*/
|
||||
},
|
||||
async (ctx, req, res) => ctx
|
||||
]
|
||||
}
|
||||
|
||||
type Compose2<B extends object, A extends object, D, C> = D & B extends A & C ? B extends C ? ProcDecorator<D & B, A> : never : never;
|
||||
@@ -134,7 +156,7 @@ 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>, PostProcDecorator<any>];
|
||||
|
||||
type PackChain<T> =
|
||||
export 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> :
|
||||
|
||||
Reference in New Issue
Block a user