gg-express 1.0.79 → 1.0.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import GGExpress from "./GGExpress";
2
+ import GGExpressV2 from "./v2/GGExpressV2";
3
+ export { GGExpress, GGExpressV2 };
package/dist/main.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GGExpressV2 = exports.GGExpress = void 0;
7
+ const GGExpress_1 = __importDefault(require("./GGExpress"));
8
+ exports.GGExpress = GGExpress_1.default;
9
+ const GGExpressV2_1 = __importDefault(require("./v2/GGExpressV2"));
10
+ exports.GGExpressV2 = GGExpressV2_1.default;
package/dist/run.test.js CHANGED
@@ -82,9 +82,7 @@ function run() {
82
82
  },
83
83
  });
84
84
  });
85
- ggapp.get(["/api/seed/memo/id",
86
- "/api/hotel/memo/id"
87
- ], {
85
+ ggapp.get(["/api/seed/memo/id", "/api/hotel/memo/id"], {
88
86
  requireParams: {
89
87
  parameter: { files: "string[]" },
90
88
  dataType: "arrayObject",
@@ -0,0 +1,38 @@
1
+ import { NextFunction, Request, Response } from "express";
2
+ import Express from "express-serve-static-core";
3
+ import { convertRoot, InputParent } from "./typeResolver";
4
+ export type Method = "get" | "post" | "put" | "delete";
5
+ type ReqByMethod<M extends Method, RQ extends InputParent> = M extends "get" ? convertRoot<MyRequestQuery<RQ>> : convertRoot<MyRequestBody<RQ>>;
6
+ type MyRequestQuery<T extends InputParent> = Request<{}, {}, {}, T, {}>;
7
+ type MyRequestBody<T extends InputParent> = Request<{}, {}, T, {}, {}>;
8
+ type MyResponse<T extends InputParent> = Response<{
9
+ status: "SUCCESS" | "ERROR" | "SERVER_ERROR";
10
+ message: string;
11
+ data: convertRoot<T>;
12
+ }>;
13
+ export default class GGExpressV2<appName extends string> {
14
+ express: Express.Express;
15
+ private outputPath;
16
+ appName: string;
17
+ constructor(app: Express.Express, appName: appName, outputPath: string[]);
18
+ private rootMethod;
19
+ get<RQ extends InputParent, RS extends InputParent>(url: `/api/${appName | "seed"}/${string}` | `/api/${appName | "seed"}/${string}`[], options: {
20
+ requireParams: RQ;
21
+ responseStructure: RS;
22
+ }, ...middlewares: Array<(req: ReqByMethod<"get", RQ>, res: MyResponse<RS>, next: NextFunction) => any>): Express.Express;
23
+ post<RQ extends InputParent, RS extends InputParent>(url: `/api/${appName | "seed"}/${string}` | `/api/${appName | "seed"}/${string}`[], options: {
24
+ requireParams: RQ;
25
+ responseStructure: RS;
26
+ }, ...middlewares: Array<(req: ReqByMethod<"post", RQ>, res: MyResponse<RS>, next: NextFunction) => any>): Express.Express;
27
+ put<RQ extends InputParent, RS extends InputParent>(url: `/api/${appName | "seed"}/${string}` | `/api/${appName | "seed"}/${string}`[], options: {
28
+ requireParams: RQ;
29
+ responseStructure: RS;
30
+ }, ...middlewares: Array<(req: ReqByMethod<"put", RQ>, res: MyResponse<RS>, next: NextFunction) => any>): Express.Express;
31
+ delete<RQ extends InputParent, RS extends InputParent>(url: `/api/${appName | "seed"}/${string}` | `/api/${appName | "seed"}/${string}`[], options: {
32
+ requireParams: RQ;
33
+ responseStructure: RS;
34
+ }, ...middlewares: Array<(req: ReqByMethod<"delete", RQ>, res: MyResponse<RS>, next: NextFunction) => any>): Express.Express;
35
+ generateAPIFiles(): Promise<void>;
36
+ private generateStaticRouteFile;
37
+ }
38
+ export {};
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const generateStaticRouteFileV2_1 = require("./generateStaticRouteFileV2");
17
+ const path_1 = __importDefault(require("path"));
18
+ const generateGGApi_v2_1 = require("./generateGGApi_v2");
19
+ const myExpressRouteList = [];
20
+ class GGExpressV2 {
21
+ constructor(app, appName, outputPath) {
22
+ this.express = app;
23
+ this.outputPath = outputPath;
24
+ this.appName = appName;
25
+ }
26
+ rootMethod(method, url, options, ...middlewares) {
27
+ let tempURL;
28
+ if (Array.isArray(url)) {
29
+ tempURL = url;
30
+ }
31
+ else {
32
+ tempURL = [url];
33
+ }
34
+ tempURL.map((url) => {
35
+ myExpressRouteList.push({
36
+ method: method,
37
+ url: url,
38
+ requireParams: options.requireParams,
39
+ responseStructure: Object.assign({}, options.responseStructure),
40
+ });
41
+ });
42
+ return this.express[method](url,
43
+ // Express handler ต้องกว้าง
44
+ (req, res, next) => next(),
45
+ // แต่ middleware ของคุณจะถูก wrap ทีละตัว
46
+ ...middlewares.map((mw) => ((req, res, next) => mw(req, res, next))));
47
+ }
48
+ get(url, options, ...middlewares) {
49
+ return this.rootMethod("get", url, options, ...middlewares);
50
+ }
51
+ post(url, options, ...middlewares) {
52
+ return this.rootMethod("post", url, options, ...middlewares);
53
+ }
54
+ put(url, options, ...middlewares) {
55
+ return this.rootMethod("put", url, options, ...middlewares);
56
+ }
57
+ delete(url, options, ...middlewares) {
58
+ return this.rootMethod("delete", url, options, ...middlewares);
59
+ }
60
+ generateAPIFiles() {
61
+ return __awaiter(this, void 0, void 0, function* () {
62
+ yield this.generateStaticRouteFile();
63
+ console.log(`GGExpressV2.ts : ${this.appName} - generate staticRoute done.`);
64
+ });
65
+ }
66
+ generateStaticRouteFile() {
67
+ return __awaiter(this, void 0, void 0, function* () {
68
+ const staticRouteCode = (0, generateStaticRouteFileV2_1.generateStaticRouteFileV2)(this.appName, myExpressRouteList);
69
+ for (let row of this.outputPath) {
70
+ fs_1.default.writeFileSync(path_1.default.join(row, `staticRouteInterface_${this.appName}_v2.ts`), staticRouteCode);
71
+ yield (0, generateGGApi_v2_1.generateGGApi_v2)(this.appName, path_1.default.join(row, `apiConnector_${this.appName}_v2.ts`));
72
+ }
73
+ });
74
+ }
75
+ }
76
+ exports.default = GGExpressV2;
@@ -0,0 +1 @@
1
+ export declare function generateGGApi_v2(appName: string, filePathWithFileName: string): void;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateGGApi_v2 = generateGGApi_v2;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ function generateGGApi_v2(appName, filePathWithFileName) {
9
+ const tempInterfaceName = `staticRouteInterface_${appName}_v2`;
10
+ const code = `
11
+ import axios from "axios"
12
+ import { ${tempInterfaceName} } from "./${tempInterfaceName}"
13
+
14
+ export class GGApi_v2 {
15
+ constructor() {}
16
+
17
+ get<T extends keyof ${tempInterfaceName}["get"]>(
18
+ url: T,
19
+ requireParams: ${tempInterfaceName}["get"][T]["requireParams"]
20
+ ): Promise<${tempInterfaceName}["get"][T]["responseStructure"]> {
21
+ return new Promise((resolve, reject) => {
22
+ axios
23
+ .get(url as string, { params: { data: requireParams } })
24
+ .then((response) => {
25
+ resolve(response.data)
26
+ })
27
+ .catch((error) => {
28
+ reject(error)
29
+ })
30
+ })
31
+ }
32
+
33
+ post<T extends keyof ${tempInterfaceName}["post"]>(
34
+ url: T,
35
+ requireParams: ${tempInterfaceName}["post"][T]["requireParams"]
36
+ ): Promise<${tempInterfaceName}["post"][T]["responseStructure"]> {
37
+ return new Promise((resolve, reject) => {
38
+ axios
39
+ .post(url as string, { data: requireParams })
40
+ .then((response) => {
41
+ resolve(response.data)
42
+ })
43
+ .catch((error) => {
44
+ reject(error)
45
+ })
46
+ })
47
+ }
48
+ put<T extends keyof ${tempInterfaceName}["put"]>(
49
+ url: T,
50
+ requireParams: ${tempInterfaceName}["put"][T]["requireParams"]
51
+ ): Promise<${tempInterfaceName}["put"][T]["responseStructure"]> {
52
+ return new Promise((resolve, reject) => {
53
+ axios
54
+ .put(url as string, { data: requireParams })
55
+ .then((response) => {
56
+ resolve(response.data)
57
+ })
58
+ .catch((error) => {
59
+ reject(error)
60
+ })
61
+ })
62
+ }
63
+ delete<T extends keyof ${tempInterfaceName}["delete"]>(
64
+ url: T,
65
+ requireParams: ${tempInterfaceName}["delete"][T]["requireParams"]
66
+ ): Promise<${tempInterfaceName}["delete"][T]["responseStructure"]> {
67
+ return new Promise((resolve, reject) => {
68
+ axios
69
+ .delete(url as string, { data: { data: requireParams } })
70
+ .then((response) => {
71
+ resolve(response.data)
72
+ })
73
+ .catch((error) => {
74
+ reject(error)
75
+ })
76
+ })
77
+ }
78
+ }
79
+ `;
80
+ fs_1.default.writeFileSync(filePathWithFileName, code);
81
+ }
@@ -0,0 +1,19 @@
1
+ import { Method } from "./GGExpressV2";
2
+ import { InputParent } from "./typeResolver";
3
+ type routeList = {
4
+ method: Method;
5
+ url: string;
6
+ requireParams: InputParent;
7
+ responseStructure: InputParent;
8
+ };
9
+ type PrepareRouteStucture = {
10
+ [K in Method]: {
11
+ [key in string]: {
12
+ requireParams: string;
13
+ responseStructure: string;
14
+ };
15
+ };
16
+ };
17
+ export declare function prepareExportInterfaceData(data: routeList[]): PrepareRouteStucture;
18
+ export declare function generateStaticRouteFileV2(appName: string, data: routeList[]): string;
19
+ export {};
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareExportInterfaceData = prepareExportInterfaceData;
4
+ exports.generateStaticRouteFileV2 = generateStaticRouteFileV2;
5
+ function prepareExportInterfaceData(data) {
6
+ let prepareRoute = {
7
+ get: {},
8
+ post: {},
9
+ put: {},
10
+ delete: {},
11
+ };
12
+ prepareRoute = extract(data, prepareRoute);
13
+ return prepareRoute;
14
+ }
15
+ function extract(allData, prepareRoute) {
16
+ for (const route of allData) {
17
+ prepareRoute[route.method][route.url] = {
18
+ requireParams: parentInputToCode(route.requireParams),
19
+ responseStructure: parentInputToCode(route.responseStructure),
20
+ };
21
+ }
22
+ return prepareRoute;
23
+ }
24
+ function parentInputToCode(data) {
25
+ const recursiveExtract = (target, parentKeyName) => {
26
+ let result = "";
27
+ // check nest pimitive -------------------
28
+ if (typeof target === "string") {
29
+ const isUndefinedAble = target.includes("?")
30
+ ? "undefined"
31
+ : "";
32
+ const isNullAble = target.includes("~") ? "null" : "";
33
+ const isArray = target.includes("[]") ? "[]" : "";
34
+ const varType = target.replace(/\[\]|\?|\~/g, "");
35
+ let result = [`${varType}`, isUndefinedAble, isNullAble]
36
+ .filter((row) => row !== "")
37
+ .join(" | ");
38
+ if (isArray)
39
+ result = `(${result})[]`;
40
+ else
41
+ result;
42
+ return `${parentKeyName} : ${result}`;
43
+ }
44
+ // check object ---------------------------
45
+ else if (typeof target === "object" && Array.isArray(target) === false) {
46
+ const keyList = Object.keys(target);
47
+ const tempResult = [];
48
+ for (const keyName of keyList) {
49
+ //@ts-ignore
50
+ const recusiveResult = recursiveExtract(target[keyName], keyName);
51
+ tempResult.push(recusiveResult);
52
+ }
53
+ result = `${result} \n ${parentKeyName} : { ${tempResult.join(",")} }`;
54
+ }
55
+ // check array ---------------------------
56
+ else if (Array.isArray(target)) {
57
+ // enum
58
+ if (typeof target[0] === "number" ||
59
+ typeof target[0] === "string" ||
60
+ target[0] === null ||
61
+ target[0] === undefined)
62
+ return `${parentKeyName} : [${target
63
+ .map((word) => ` "${word}" `)
64
+ .join(",")}] `;
65
+ // nest or input nest
66
+ else if (typeof target[0] === "object")
67
+ for (const row of target) {
68
+ result = result + " " + recursiveExtract(row, parentKeyName) + "[] ";
69
+ }
70
+ }
71
+ return `${result}`;
72
+ };
73
+ const result = recursiveExtract(data, "data");
74
+ return `${result} `;
75
+ }
76
+ function generateStaticRouteFileV2(appName, data) {
77
+ const interfaceName = `staticRouteInterface_${appName}_v2`;
78
+ const generateEachUrl = (data) => {
79
+ const keyName = Object.keys(data);
80
+ return keyName
81
+ .map((keyName) => `
82
+ "${keyName}" : {
83
+ requireParams : { ${data[keyName].requireParams} },
84
+ responseStructure : { ${data[keyName].responseStructure},
85
+ status: "SUCCESS" | "ERROR",
86
+ message: string }
87
+ }`)
88
+ .join(", ");
89
+ };
90
+ const preparedData = prepareExportInterfaceData(data);
91
+ let code = `
92
+ export interface ${interfaceName} {
93
+ get : { ${generateEachUrl(preparedData.get)} },
94
+ post : { ${generateEachUrl(preparedData.post)} },
95
+ put : { ${generateEachUrl(preparedData.put)} },
96
+ delete : { ${generateEachUrl(preparedData.delete)} },
97
+ }
98
+ `;
99
+ return code;
100
+ }
@@ -0,0 +1,8 @@
1
+ import { staticRouteInterface_hotel_v2 } from "./staticRouteInterface_hotel_v2";
2
+ export declare class GGApi_v2 {
3
+ constructor();
4
+ get<T extends keyof staticRouteInterface_hotel_v2["get"]>(url: T, requireParams: staticRouteInterface_hotel_v2["get"][T]["requireParams"]): Promise<staticRouteInterface_hotel_v2["get"][T]["responseStructure"]>;
5
+ post<T extends keyof staticRouteInterface_hotel_v2["post"]>(url: T, requireParams: staticRouteInterface_hotel_v2["post"][T]["requireParams"]): Promise<staticRouteInterface_hotel_v2["post"][T]["responseStructure"]>;
6
+ put<T extends keyof staticRouteInterface_hotel_v2["put"]>(url: T, requireParams: staticRouteInterface_hotel_v2["put"][T]["requireParams"]): Promise<staticRouteInterface_hotel_v2["put"][T]["responseStructure"]>;
7
+ delete<T extends keyof staticRouteInterface_hotel_v2["delete"]>(url: T, requireParams: staticRouteInterface_hotel_v2["delete"][T]["requireParams"]): Promise<staticRouteInterface_hotel_v2["delete"][T]["responseStructure"]>;
8
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GGApi_v2 = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class GGApi_v2 {
9
+ constructor() { }
10
+ get(url, requireParams) {
11
+ return new Promise((resolve, reject) => {
12
+ axios_1.default
13
+ .get(url, { params: { data: requireParams } })
14
+ .then((response) => {
15
+ resolve(response.data);
16
+ })
17
+ .catch((error) => {
18
+ reject(error);
19
+ });
20
+ });
21
+ }
22
+ post(url, requireParams) {
23
+ return new Promise((resolve, reject) => {
24
+ axios_1.default
25
+ .post(url, { data: requireParams })
26
+ .then((response) => {
27
+ resolve(response.data);
28
+ })
29
+ .catch((error) => {
30
+ reject(error);
31
+ });
32
+ });
33
+ }
34
+ put(url, requireParams) {
35
+ return new Promise((resolve, reject) => {
36
+ axios_1.default
37
+ .put(url, { data: requireParams })
38
+ .then((response) => {
39
+ resolve(response.data);
40
+ })
41
+ .catch((error) => {
42
+ reject(error);
43
+ });
44
+ });
45
+ }
46
+ delete(url, requireParams) {
47
+ return new Promise((resolve, reject) => {
48
+ axios_1.default
49
+ .delete(url, { data: { data: requireParams } })
50
+ .then((response) => {
51
+ resolve(response.data);
52
+ })
53
+ .catch((error) => {
54
+ reject(error);
55
+ });
56
+ });
57
+ }
58
+ }
59
+ exports.GGApi_v2 = GGApi_v2;
@@ -0,0 +1,42 @@
1
+ export interface staticRouteInterface_hotel_v2 {
2
+ get: {
3
+ "/api/hotel/users/id": {
4
+ requireParams: {
5
+ data: {
6
+ id: number;
7
+ };
8
+ };
9
+ responseStructure: {
10
+ data: {
11
+ userData: {
12
+ id: number;
13
+ name: string;
14
+ }[];
15
+ };
16
+ status: "SUCCESS" | "ERROR";
17
+ message: string;
18
+ };
19
+ };
20
+ };
21
+ post: {
22
+ "/api/hotel/booking/id": {
23
+ requireParams: {
24
+ data: {
25
+ id: number;
26
+ };
27
+ };
28
+ responseStructure: {
29
+ data: {
30
+ bookingData: {
31
+ id: number;
32
+ roomNumber: string;
33
+ };
34
+ };
35
+ status: "SUCCESS" | "ERROR";
36
+ message: string;
37
+ };
38
+ };
39
+ };
40
+ put: {};
41
+ delete: {};
42
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const express_1 = __importDefault(require("express"));
16
+ const GGExpressV2_1 = __importDefault(require("./GGExpressV2"));
17
+ const typeResolver_1 = require("./typeResolver");
18
+ const apiConnector_hotel_v2_1 = require("./output/apiConnector_hotel_v2");
19
+ // import { GGApi_v2 } from "./output/apiConnector_hotel_v2"
20
+ function run() {
21
+ const app = (0, express_1.default)();
22
+ const ggapp = new GGExpressV2_1.default(app, "hotel", ["./output"]);
23
+ ggapp.get("/api/hotel/users/id", {
24
+ requireParams: {
25
+ id: "number",
26
+ },
27
+ responseStructure: {
28
+ userData: (0, typeResolver_1.toArray)({
29
+ id: "number",
30
+ name: "string",
31
+ }),
32
+ },
33
+ }, (req, res, next) => {
34
+ console.log(req.query);
35
+ return res.json({
36
+ message: "",
37
+ status: "SUCCESS",
38
+ data: { userData: [] },
39
+ });
40
+ });
41
+ ggapp.post("/api/hotel/booking/id", {
42
+ requireParams: {
43
+ id: "number",
44
+ },
45
+ responseStructure: {
46
+ bookingData: {
47
+ id: "number",
48
+ roomNumber: "string",
49
+ },
50
+ },
51
+ }, (req, res, next) => {
52
+ console.log(req.query);
53
+ return res.json({
54
+ message: "",
55
+ status: "SUCCESS",
56
+ data: {
57
+ bookingData: {
58
+ id: 2,
59
+ roomNumber: "dd",
60
+ },
61
+ },
62
+ });
63
+ });
64
+ app.listen(3002, () => __awaiter(this, void 0, void 0, function* () {
65
+ yield ggapp.generateAPIFiles();
66
+ console.log("done");
67
+ process.exit(0);
68
+ }));
69
+ }
70
+ run();
71
+ const x = new apiConnector_hotel_v2_1.GGApi_v2();
72
+ x.post("/api/hotel/booking/id", {
73
+ data: { id: 1 },
74
+ }).then((response) => {
75
+ response.status === "ERROR";
76
+ response.data.bookingData;
77
+ response.data.bookingData.roomNumber;
78
+ });
@@ -0,0 +1,23 @@
1
+ type BaseType = "string" | "number" | "string[]" | "number[]";
2
+ export type CustomType = BaseType | `${BaseType}?` | `${BaseType}~` | `${BaseType}?~`;
3
+ export type InputNest = {
4
+ [key in string]: CustomType;
5
+ };
6
+ type InputEnum = string[] | number[];
7
+ export type InputParent = {
8
+ [key in string]: CustomType | InputNest | InputNest[] | InputParent | InputParent[] | InputEnum;
9
+ };
10
+ type ResolveBase<T extends BaseType> = T extends "string" ? string : T extends "string[]" ? string[] : T extends "number" ? number : T extends "number[]" ? number[] : never;
11
+ export declare const toArray: <T extends InputParent>(input: T) => T[];
12
+ type ResolveType<T> = T extends `${infer B}?~` ? ResolveBase<B & BaseType> | null | undefined : T extends `${infer B}?` ? ResolveBase<B & BaseType> | undefined : T extends `${infer B}~` ? ResolveBase<B & BaseType> | null : ResolveBase<T & BaseType>;
13
+ type convertNest<T> = ResolveType<T>;
14
+ type checkEnum<T> = T extends InputEnum ? T[number] : convertNest<T>;
15
+ type convertChildObjet<T> = {
16
+ [K in keyof T]: chooseType<T[K]>;
17
+ };
18
+ type convertObject<T> = T extends object ? convertChildObjet<T> : checkEnum<T>;
19
+ type chooseType<T> = T extends InputEnum ? T[number] : convertObject<T>;
20
+ export type convertRoot<T> = {
21
+ [K in keyof T]: chooseType<T[K]>;
22
+ };
23
+ export {};
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toArray = void 0;
4
+ const toArray = (input) => {
5
+ return [input];
6
+ };
7
+ exports.toArray = toArray;
8
+ // const data = {} as convertRoot<typeof req2>
9
+ // data.booking.bookingStatus
10
+ // data.status
11
+ // const id = data.booking.id
12
+ // const roomNumber = data.booking.bookingStatus === data.booking.bookingStatus
13
+ // data.rooms.map((row) => row.status === "OFF")
14
+ // data.data.level_1.level_2.id
15
+ // data.booking.createTime
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "gg-express",
3
- "version": "1.0.79",
3
+ "version": "1.0.81",
4
4
  "description": "",
5
- "main": "dist/GGExpress.js",
5
+ "main": "dist/main.js",
6
6
  "scripts": {
7
7
  "build": "tsc && npm version patch --no-git-tag-version",
8
8
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/express": "^5.0.0",
27
- "typescript": "^5.8.3"
27
+ "typescript": "^5.9.3"
28
28
  },
29
29
  "keywords": [
30
30
  "express",
package/src/main.ts ADDED
@@ -0,0 +1,3 @@
1
+ import GGExpress from "./GGExpress"
2
+ import GGExpressV2 from "./v2/GGExpressV2"
3
+ export { GGExpress, GGExpressV2 }
package/src/run.test.ts CHANGED
@@ -88,10 +88,8 @@ function run() {
88
88
  }
89
89
  )
90
90
 
91
- ggapp.get(
92
- ["/api/seed/memo/id",
93
- "/api/hotel/memo/id"
94
- ],
91
+ ggapp.get(
92
+ ["/api/seed/memo/id", "/api/hotel/memo/id"],
95
93
  {
96
94
  requireParams: {
97
95
  parameter: { files: "string[]" },
@@ -0,0 +1,190 @@
1
+ import { NextFunction, Request, Response } from "express"
2
+ import Express from "express-serve-static-core"
3
+ import fs from "fs"
4
+ import { convertRoot, InputParent } from "./typeResolver"
5
+ import { generateStaticRouteFileV2 } from "./generateStaticRouteFileV2"
6
+ import path from "path"
7
+ import { generateGGApi_v2 } from "./generateGGApi_v2"
8
+
9
+ type Unarray<T> = T extends (infer U)[] ? U : T
10
+ export type Method = "get" | "post" | "put" | "delete"
11
+
12
+ type ReqByMethod<M extends Method, RQ extends InputParent> = M extends "get"
13
+ ? convertRoot<MyRequestQuery<RQ>>
14
+ : convertRoot<MyRequestBody<RQ>>
15
+
16
+ type MyRequestQuery<T extends InputParent> = Request<{}, {}, {}, T, {}>
17
+ type MyRequestBody<T extends InputParent> = Request<{}, {}, T, {}, {}>
18
+
19
+ type MyResponse<T extends InputParent> = Response<{
20
+ status: "SUCCESS" | "ERROR" | "SERVER_ERROR"
21
+ message: string
22
+ data: convertRoot<T>
23
+ }>
24
+
25
+ const myExpressRouteList: {
26
+ method: "get" | "post" | "put" | "delete"
27
+ url: string
28
+ requireParams: InputParent
29
+ responseStructure: InputParent
30
+ }[] = []
31
+
32
+ export default class GGExpressV2<appName extends string> {
33
+ public express: Express.Express
34
+ private outputPath: string[]
35
+ appName: string
36
+ constructor(app: Express.Express, appName: appName, outputPath: string[]) {
37
+ this.express = app
38
+ this.outputPath = outputPath
39
+ this.appName = appName
40
+ }
41
+
42
+ private rootMethod<
43
+ M extends Method,
44
+ RQ extends InputParent,
45
+ RS extends InputParent
46
+ >(
47
+ method: M,
48
+ url: string | string[],
49
+ options: {
50
+ requireParams: RQ
51
+ responseStructure: RS
52
+ },
53
+ ...middlewares: Array<
54
+ (
55
+ req: ReqByMethod<M, RQ>,
56
+ res: MyResponse<RS>,
57
+ next: NextFunction
58
+ ) => void | Promise<void>
59
+ >
60
+ ): Express.Express {
61
+ let tempURL
62
+ if (Array.isArray(url)) {
63
+ tempURL = url
64
+ } else {
65
+ tempURL = [url]
66
+ }
67
+ tempURL.map((url) => {
68
+ myExpressRouteList.push({
69
+ method: method,
70
+ url: url,
71
+ requireParams: options.requireParams,
72
+ responseStructure: {
73
+ ...options.responseStructure,
74
+ },
75
+ })
76
+ })
77
+
78
+ return this.express[method](
79
+ url,
80
+ // Express handler ต้องกว้าง
81
+ (req: Request, res: Response, next: NextFunction) => next(),
82
+
83
+ // แต่ middleware ของคุณจะถูก wrap ทีละตัว
84
+ ...middlewares.map(
85
+ (mw) =>
86
+ ((req: Request, res: Response, next: NextFunction) =>
87
+ mw(
88
+ req as unknown as ReqByMethod<M, RQ>,
89
+ res as MyResponse<RS>,
90
+ next
91
+ )) as Express.RequestHandler
92
+ )
93
+ )
94
+ }
95
+ get<RQ extends InputParent, RS extends InputParent>(
96
+ url:
97
+ | `/api/${appName | "seed"}/${string}`
98
+ | `/api/${appName | "seed"}/${string}`[],
99
+ options: {
100
+ requireParams: RQ
101
+ responseStructure: RS
102
+ },
103
+ ...middlewares: Array<
104
+ (
105
+ req: ReqByMethod<"get", RQ>,
106
+ res: MyResponse<RS>,
107
+ next: NextFunction
108
+ ) => any
109
+ >
110
+ ) {
111
+ return this.rootMethod("get", url, options, ...middlewares)
112
+ }
113
+
114
+ post<RQ extends InputParent, RS extends InputParent>(
115
+ url:
116
+ | `/api/${appName | "seed"}/${string}`
117
+ | `/api/${appName | "seed"}/${string}`[],
118
+ options: {
119
+ requireParams: RQ
120
+ responseStructure: RS
121
+ },
122
+ ...middlewares: Array<
123
+ (
124
+ req: ReqByMethod<"post", RQ>,
125
+ res: MyResponse<RS>,
126
+ next: NextFunction
127
+ ) => any
128
+ >
129
+ ) {
130
+ return this.rootMethod("post", url, options, ...middlewares)
131
+ }
132
+ put<RQ extends InputParent, RS extends InputParent>(
133
+ url:
134
+ | `/api/${appName | "seed"}/${string}`
135
+ | `/api/${appName | "seed"}/${string}`[],
136
+ options: {
137
+ requireParams: RQ
138
+ responseStructure: RS
139
+ },
140
+ ...middlewares: Array<
141
+ (
142
+ req: ReqByMethod<"put", RQ>,
143
+ res: MyResponse<RS>,
144
+ next: NextFunction
145
+ ) => any
146
+ >
147
+ ) {
148
+ return this.rootMethod("put", url, options, ...middlewares)
149
+ }
150
+ delete<RQ extends InputParent, RS extends InputParent>(
151
+ url:
152
+ | `/api/${appName | "seed"}/${string}`
153
+ | `/api/${appName | "seed"}/${string}`[],
154
+ options: {
155
+ requireParams: RQ
156
+ responseStructure: RS
157
+ },
158
+ ...middlewares: Array<
159
+ (
160
+ req: ReqByMethod<"delete", RQ>,
161
+ res: MyResponse<RS>,
162
+ next: NextFunction
163
+ ) => any
164
+ >
165
+ ) {
166
+ return this.rootMethod("delete", url, options, ...middlewares)
167
+ }
168
+ public async generateAPIFiles() {
169
+ await this.generateStaticRouteFile()
170
+ console.log(`GGExpressV2.ts : ${this.appName} - generate staticRoute done.`)
171
+ }
172
+
173
+ private async generateStaticRouteFile() {
174
+ const staticRouteCode = generateStaticRouteFileV2(
175
+ this.appName,
176
+ myExpressRouteList
177
+ )
178
+
179
+ for (let row of this.outputPath) {
180
+ fs.writeFileSync(
181
+ path.join(row, `staticRouteInterface_${this.appName}_v2.ts`),
182
+ staticRouteCode
183
+ )
184
+ await generateGGApi_v2(
185
+ this.appName,
186
+ path.join(row, `apiConnector_${this.appName}_v2.ts`)
187
+ )
188
+ }
189
+ }
190
+ }
@@ -0,0 +1,79 @@
1
+ import fs from "fs"
2
+
3
+ export function generateGGApi_v2(
4
+ appName: string,
5
+ filePathWithFileName: string
6
+ ) {
7
+ const tempInterfaceName = `staticRouteInterface_${appName}_v2`
8
+ const code = `
9
+ import axios from "axios"
10
+ import { ${tempInterfaceName} } from "./${tempInterfaceName}"
11
+
12
+ export class GGApi_v2 {
13
+ constructor() {}
14
+
15
+ get<T extends keyof ${tempInterfaceName}["get"]>(
16
+ url: T,
17
+ requireParams: ${tempInterfaceName}["get"][T]["requireParams"]
18
+ ): Promise<${tempInterfaceName}["get"][T]["responseStructure"]> {
19
+ return new Promise((resolve, reject) => {
20
+ axios
21
+ .get(url as string, { params: { data: requireParams } })
22
+ .then((response) => {
23
+ resolve(response.data)
24
+ })
25
+ .catch((error) => {
26
+ reject(error)
27
+ })
28
+ })
29
+ }
30
+
31
+ post<T extends keyof ${tempInterfaceName}["post"]>(
32
+ url: T,
33
+ requireParams: ${tempInterfaceName}["post"][T]["requireParams"]
34
+ ): Promise<${tempInterfaceName}["post"][T]["responseStructure"]> {
35
+ return new Promise((resolve, reject) => {
36
+ axios
37
+ .post(url as string, { data: requireParams })
38
+ .then((response) => {
39
+ resolve(response.data)
40
+ })
41
+ .catch((error) => {
42
+ reject(error)
43
+ })
44
+ })
45
+ }
46
+ put<T extends keyof ${tempInterfaceName}["put"]>(
47
+ url: T,
48
+ requireParams: ${tempInterfaceName}["put"][T]["requireParams"]
49
+ ): Promise<${tempInterfaceName}["put"][T]["responseStructure"]> {
50
+ return new Promise((resolve, reject) => {
51
+ axios
52
+ .put(url as string, { data: requireParams })
53
+ .then((response) => {
54
+ resolve(response.data)
55
+ })
56
+ .catch((error) => {
57
+ reject(error)
58
+ })
59
+ })
60
+ }
61
+ delete<T extends keyof ${tempInterfaceName}["delete"]>(
62
+ url: T,
63
+ requireParams: ${tempInterfaceName}["delete"][T]["requireParams"]
64
+ ): Promise<${tempInterfaceName}["delete"][T]["responseStructure"]> {
65
+ return new Promise((resolve, reject) => {
66
+ axios
67
+ .delete(url as string, { data: { data: requireParams } })
68
+ .then((response) => {
69
+ resolve(response.data)
70
+ })
71
+ .catch((error) => {
72
+ reject(error)
73
+ })
74
+ })
75
+ }
76
+ }
77
+ `
78
+ fs.writeFileSync(filePathWithFileName, code)
79
+ }
@@ -0,0 +1,127 @@
1
+ import { Method } from "./GGExpressV2"
2
+ import { CustomType, InputNest, InputParent, toArray } from "./typeResolver"
3
+ import fs from "fs"
4
+ type routeList = {
5
+ method: Method
6
+ url: string
7
+ requireParams: InputParent
8
+ responseStructure: InputParent
9
+ }
10
+
11
+ type PrepareRouteStucture = {
12
+ [K in Method]: {
13
+ [key in string]: {
14
+ requireParams: string
15
+ responseStructure: string
16
+ }
17
+ }
18
+ }
19
+
20
+ type MyResponse = {
21
+ [url in string]: {}
22
+ }
23
+ export function prepareExportInterfaceData(data: routeList[]) {
24
+ let prepareRoute: PrepareRouteStucture = {
25
+ get: {},
26
+ post: {},
27
+ put: {},
28
+ delete: {},
29
+ }
30
+ prepareRoute = extract(data, prepareRoute)
31
+ return prepareRoute
32
+ }
33
+ function extract(
34
+ allData: routeList[],
35
+ prepareRoute: PrepareRouteStucture
36
+ ): PrepareRouteStucture {
37
+ for (const route of allData) {
38
+ prepareRoute[route.method][route.url] = {
39
+ requireParams: parentInputToCode(route.requireParams),
40
+ responseStructure: parentInputToCode(route.responseStructure),
41
+ }
42
+ }
43
+
44
+ return prepareRoute
45
+ }
46
+
47
+ function parentInputToCode(data: InputParent) {
48
+ const recursiveExtract = (target: InputParent, parentKeyName: string) => {
49
+ let result = ""
50
+ // check nest pimitive -------------------
51
+ if (typeof target === "string") {
52
+ const isUndefinedAble = (target as string).includes("?")
53
+ ? "undefined"
54
+ : ""
55
+ const isNullAble = (target as string).includes("~") ? "null" : ""
56
+ const isArray = (target as string).includes("[]") ? "[]" : ""
57
+ const varType = (target as string).replace(/\[\]|\?|\~/g, "")
58
+ let result = [`${varType}`, isUndefinedAble, isNullAble]
59
+ .filter((row) => row !== "")
60
+ .join(" | ")
61
+ if (isArray) result = `(${result})[]`
62
+ else result
63
+ return `${parentKeyName} : ${result}`
64
+ }
65
+
66
+ // check object ---------------------------
67
+ else if (typeof target === "object" && Array.isArray(target) === false) {
68
+ const keyList = Object.keys(target)
69
+ const tempResult = []
70
+ for (const keyName of keyList) {
71
+ //@ts-ignore
72
+ const recusiveResult = recursiveExtract(target[keyName], keyName)
73
+ tempResult.push(recusiveResult)
74
+ }
75
+ result = `${result} \n ${parentKeyName} : { ${tempResult.join(",")} }`
76
+ }
77
+
78
+ // check array ---------------------------
79
+ else if (Array.isArray(target)) {
80
+ // enum
81
+ if (
82
+ typeof target[0] === "number" ||
83
+ typeof target[0] === "string" ||
84
+ target[0] === null ||
85
+ target[0] === undefined
86
+ )
87
+ return `${parentKeyName} : [${(target as string[])
88
+ .map((word) => ` "${word}" `)
89
+ .join(",")}] `
90
+ // nest or input nest
91
+ else if (typeof target[0] === "object")
92
+ for (const row of target as InputNest[] | InputParent[]) {
93
+ result = result + " " + recursiveExtract(row, parentKeyName) + "[] "
94
+ }
95
+ }
96
+ return `${result}`
97
+ }
98
+ const result = recursiveExtract(data, "data")
99
+ return `${result} `
100
+ }
101
+ export function generateStaticRouteFileV2(appName: string, data: routeList[]) {
102
+ const interfaceName = `staticRouteInterface_${appName}_v2`
103
+ const generateEachUrl = (data: PrepareRouteStucture[Method]) => {
104
+ const keyName = Object.keys(data)
105
+ return keyName
106
+ .map(
107
+ (keyName) => `
108
+ "${keyName}" : {
109
+ requireParams : { ${data[keyName].requireParams} },
110
+ responseStructure : { ${data[keyName].responseStructure},
111
+ status: "SUCCESS" | "ERROR",
112
+ message: string }
113
+ }`
114
+ )
115
+ .join(", ")
116
+ }
117
+ const preparedData = prepareExportInterfaceData(data)
118
+ let code = `
119
+ export interface ${interfaceName} {
120
+ get : { ${generateEachUrl(preparedData.get)} },
121
+ post : { ${generateEachUrl(preparedData.post)} },
122
+ put : { ${generateEachUrl(preparedData.put)} },
123
+ delete : { ${generateEachUrl(preparedData.delete)} },
124
+ }
125
+ `
126
+ return code
127
+ }
@@ -0,0 +1,69 @@
1
+
2
+ import axios from "axios"
3
+ import { staticRouteInterface_hotel_v2 } from "./staticRouteInterface_hotel_v2"
4
+
5
+ export class GGApi_v2 {
6
+ constructor() {}
7
+
8
+ get<T extends keyof staticRouteInterface_hotel_v2["get"]>(
9
+ url: T,
10
+ requireParams: staticRouteInterface_hotel_v2["get"][T]["requireParams"]
11
+ ): Promise<staticRouteInterface_hotel_v2["get"][T]["responseStructure"]> {
12
+ return new Promise((resolve, reject) => {
13
+ axios
14
+ .get(url as string, { params: { data: requireParams } })
15
+ .then((response) => {
16
+ resolve(response.data)
17
+ })
18
+ .catch((error) => {
19
+ reject(error)
20
+ })
21
+ })
22
+ }
23
+
24
+ post<T extends keyof staticRouteInterface_hotel_v2["post"]>(
25
+ url: T,
26
+ requireParams: staticRouteInterface_hotel_v2["post"][T]["requireParams"]
27
+ ): Promise<staticRouteInterface_hotel_v2["post"][T]["responseStructure"]> {
28
+ return new Promise((resolve, reject) => {
29
+ axios
30
+ .post(url as string, { data: requireParams })
31
+ .then((response) => {
32
+ resolve(response.data)
33
+ })
34
+ .catch((error) => {
35
+ reject(error)
36
+ })
37
+ })
38
+ }
39
+ put<T extends keyof staticRouteInterface_hotel_v2["put"]>(
40
+ url: T,
41
+ requireParams: staticRouteInterface_hotel_v2["put"][T]["requireParams"]
42
+ ): Promise<staticRouteInterface_hotel_v2["put"][T]["responseStructure"]> {
43
+ return new Promise((resolve, reject) => {
44
+ axios
45
+ .put(url as string, { data: requireParams })
46
+ .then((response) => {
47
+ resolve(response.data)
48
+ })
49
+ .catch((error) => {
50
+ reject(error)
51
+ })
52
+ })
53
+ }
54
+ delete<T extends keyof staticRouteInterface_hotel_v2["delete"]>(
55
+ url: T,
56
+ requireParams: staticRouteInterface_hotel_v2["delete"][T]["requireParams"]
57
+ ): Promise<staticRouteInterface_hotel_v2["delete"][T]["responseStructure"]> {
58
+ return new Promise((resolve, reject) => {
59
+ axios
60
+ .delete(url as string, { data: { data: requireParams } })
61
+ .then((response) => {
62
+ resolve(response.data)
63
+ })
64
+ .catch((error) => {
65
+ reject(error)
66
+ })
67
+ })
68
+ }
69
+ }
@@ -0,0 +1,26 @@
1
+
2
+ export interface staticRouteInterface_hotel_v2 {
3
+ get : {
4
+ "/api/hotel/users/id" : {
5
+ requireParams : {
6
+ data : { id : number } },
7
+ responseStructure : {
8
+ data : {
9
+ userData : { id : number,name : string }[] } ,
10
+ status: "SUCCESS" | "ERROR",
11
+ message: string }
12
+ } },
13
+ post : {
14
+ "/api/hotel/booking/id" : {
15
+ requireParams : {
16
+ data : { id : number } },
17
+ responseStructure : {
18
+ data : {
19
+ bookingData : { id : number,roomNumber : string } } ,
20
+ status: "SUCCESS" | "ERROR",
21
+ message: string }
22
+ } },
23
+ put : { },
24
+ delete : { },
25
+ }
26
+
@@ -0,0 +1,74 @@
1
+ import express, { Express } from "express"
2
+ import GGExpressV2 from "./GGExpressV2"
3
+ import { toArray } from "./typeResolver"
4
+ import { GGApi_v2 } from "./output/apiConnector_hotel_v2"
5
+ // import { GGApi_v2 } from "./output/apiConnector_hotel_v2"
6
+ function run() {
7
+ const app = express()
8
+ const ggapp = new GGExpressV2<"hotel">(app, "hotel", ["./output"])
9
+ ggapp.get(
10
+ "/api/hotel/users/id",
11
+ {
12
+ requireParams: {
13
+ id: "number",
14
+ },
15
+ responseStructure: {
16
+ userData: toArray({
17
+ id: "number",
18
+ name: "string",
19
+ }),
20
+ },
21
+ },
22
+ (req, res, next) => {
23
+ console.log(req.query)
24
+ return res.json({
25
+ message: "",
26
+ status: "SUCCESS",
27
+ data: { userData: [] },
28
+ })
29
+ }
30
+ )
31
+ ggapp.post(
32
+ "/api/hotel/booking/id",
33
+ {
34
+ requireParams: {
35
+ id: "number",
36
+ },
37
+ responseStructure: {
38
+ bookingData: {
39
+ id: "number",
40
+ roomNumber: "string",
41
+ },
42
+ },
43
+ },
44
+ (req, res, next) => {
45
+ console.log(req.query)
46
+ return res.json({
47
+ message: "",
48
+ status: "SUCCESS",
49
+ data: {
50
+ bookingData: {
51
+ id: 2,
52
+ roomNumber: "dd",
53
+ },
54
+ },
55
+ })
56
+ }
57
+ )
58
+
59
+ app.listen(3002, async () => {
60
+ await ggapp.generateAPIFiles()
61
+ console.log("done")
62
+ process.exit(0)
63
+ })
64
+ }
65
+ run()
66
+
67
+ const x = new GGApi_v2()
68
+ x.post("/api/hotel/booking/id", {
69
+ data: { id: 1 },
70
+ }).then((response) => {
71
+ response.status === "ERROR"
72
+ response.data.bookingData
73
+ response.data.bookingData.roomNumber
74
+ })
@@ -0,0 +1,110 @@
1
+ type BaseType = "string" | "number" | "string[]" | "number[]"
2
+ export type CustomType =
3
+ | BaseType
4
+ | `${BaseType}?`
5
+ | `${BaseType}~`
6
+ | `${BaseType}?~`
7
+
8
+ export type InputNest = { [key in string]: CustomType }
9
+ type InputEnum = string[] | number[]
10
+ export type InputParent = {
11
+ [key in string]:
12
+ | CustomType
13
+ | InputNest
14
+ | InputNest[]
15
+ | InputParent
16
+ | InputParent[]
17
+ | InputEnum
18
+ }
19
+ type ResolveBase<T extends BaseType> = T extends "string"
20
+ ? string
21
+ : T extends "string[]"
22
+ ? string[]
23
+ : T extends "number"
24
+ ? number
25
+ : T extends "number[]"
26
+ ? number[]
27
+ : never
28
+
29
+ export const toArray = <T extends InputParent>(input: T) => {
30
+ return [input]
31
+ }
32
+
33
+ // let req2 = {
34
+ // booking: {
35
+ // id: "number",
36
+ // bookingGroupID: "number",
37
+ // checkInDate: "string",
38
+ // checkOutDate: "string",
39
+ // checkInTime: "string",
40
+ // checkOutTime: "string",
41
+ // roomRate: "number",
42
+ // extraBedRate: "number",
43
+ // roomNumber: "string",
44
+ // editTime: "string",
45
+ // createTime: "string",
46
+ // shiftDate: "string",
47
+ // shiftName: "string",
48
+ // userName: "string",
49
+ // bookingStatus: ["ACTIVE", "CLOSE", "CANCEL", "NO_SHOW"],
50
+ // is_specificRoom: "number",
51
+ // guestCount: "number",
52
+ // reasonToCancel: "string",
53
+ // note: "string",
54
+ // },
55
+ // status: ["ACTIVE", "DISABLE"],
56
+ // rooms: toArray({
57
+ // id: "number",
58
+ // status: ["ON", "OFF"],
59
+ // roomNumber: "string",
60
+ // log: toArray({
61
+ // time: "string",
62
+ // logger: [{ info: "string" }],
63
+ // }),
64
+ // }),
65
+
66
+ // data: {
67
+ // level_1: {
68
+ // title: "string",
69
+ // level_2: {
70
+ // id: "number",
71
+ // name: "string",
72
+ // },
73
+ // },
74
+ // },
75
+ // } as const satisfies InputParent
76
+
77
+ type ResolveType<T> =
78
+ // both null + undefined
79
+ T extends `${infer B}?~`
80
+ ? ResolveBase<B & BaseType> | null | undefined
81
+ : // undefined only
82
+ T extends `${infer B}?`
83
+ ? ResolveBase<B & BaseType> | undefined
84
+ : // null only
85
+ T extends `${infer B}~`
86
+ ? ResolveBase<B & BaseType> | null
87
+ : // plain primitive
88
+ ResolveBase<T & BaseType>
89
+
90
+ type convertNest<T> = ResolveType<T>
91
+
92
+ type checkEnum<T> = T extends InputEnum ? T[number] : convertNest<T>
93
+ type convertChildObjet<T> = {
94
+ [K in keyof T]: chooseType<T[K]>
95
+ }
96
+ type convertObject<T> = T extends object ? convertChildObjet<T> : checkEnum<T>
97
+ type chooseType<T> = T extends InputEnum ? T[number] : convertObject<T>
98
+ export type convertRoot<T> = {
99
+ [K in keyof T]: chooseType<T[K]>
100
+ }
101
+
102
+ // const data = {} as convertRoot<typeof req2>
103
+
104
+ // data.booking.bookingStatus
105
+ // data.status
106
+ // const id = data.booking.id
107
+ // const roomNumber = data.booking.bookingStatus === data.booking.bookingStatus
108
+ // data.rooms.map((row) => row.status === "OFF")
109
+ // data.data.level_1.level_2.id
110
+ // data.booking.createTime