mcl-axios-kit 0.0.1

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/Readme.md ADDED
@@ -0,0 +1 @@
1
+ # McL-Axios-Kit
@@ -0,0 +1,7 @@
1
+ import { type AxiosHeaders } from "axios";
2
+ export default abstract class McAxios {
3
+ private _axios;
4
+ constructor();
5
+ protected abstract header(): AxiosHeaders | undefined;
6
+ private bindEndpoints;
7
+ }
@@ -0,0 +1,68 @@
1
+ import axios from "axios";
2
+ import McRequest from "./McRequest";
3
+ import { ERROR_HANDLER_KEY, METHOD_META_KEY, PATH_PARAMS_KEY, REQUEST_KEY, RESPONSE_TYPE_KEY, SUCCESS_HANDLER_KEY } from "./McAxiosAnnotations";
4
+ export default class McAxios {
5
+ _axios;
6
+ constructor() {
7
+ this._axios = axios.create();
8
+ this.bindEndpoints();
9
+ }
10
+ bindEndpoints() {
11
+ const proto = Object.getPrototypeOf(this);
12
+ const names = Object.getOwnPropertyNames(proto);
13
+ // const names = Object.keys(this);
14
+ for (const name of names) {
15
+ const descriptor = Object.getOwnPropertyDescriptor(proto, name);
16
+ if (!descriptor || typeof descriptor.value !== "function")
17
+ continue;
18
+ const meta = Reflect.getMetadata(METHOD_META_KEY, proto, name);
19
+ if (!meta)
20
+ continue;
21
+ const { method, path } = meta;
22
+ const successHandler = Reflect.getMetadata(SUCCESS_HANDLER_KEY, proto, name);
23
+ const errorHandler = Reflect.getMetadata(ERROR_HANDLER_KEY, proto, name);
24
+ const requestBody = Reflect.getMetadata(REQUEST_KEY, proto, name);
25
+ const responseType = Reflect.getMetadata(RESPONSE_TYPE_KEY, proto, name);
26
+ const pathParams = Reflect.getMetadata(PATH_PARAMS_KEY, proto, name) || {};
27
+ Object.defineProperty(this, name, {
28
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
29
+ value: async (...args) => {
30
+ let url = path;
31
+ // Path param 치환
32
+ for (const [key, index] of Object.entries(pathParams)) {
33
+ const value = encodeURIComponent(args[index]);
34
+ url = url.replace(`{${key}}`, value);
35
+ }
36
+ const request = requestBody !== undefined ? args[requestBody] : undefined;
37
+ if (request !== undefined && request instanceof McRequest === false) {
38
+ const err = new Error("Request 형식이 잘못 되었습니다.");
39
+ errorHandler ? errorHandler(err) : err;
40
+ throw err;
41
+ }
42
+ const data = request !== undefined ? request.toJson() : undefined;
43
+ console.log(`param -> ${name} :: ${data}`);
44
+ try {
45
+ const response = await this._axios
46
+ .request({
47
+ method,
48
+ url,
49
+ data,
50
+ })
51
+ .catch((err) => {
52
+ throw err;
53
+ });
54
+ if (successHandler)
55
+ return successHandler(response);
56
+ if (responseType)
57
+ return new responseType(response);
58
+ return response.data;
59
+ }
60
+ catch (reqErr) {
61
+ const err = errorHandler ? errorHandler(reqErr) : reqErr;
62
+ throw reqErr;
63
+ }
64
+ },
65
+ });
66
+ }
67
+ }
68
+ }
@@ -0,0 +1,20 @@
1
+ import "reflect-metadata";
2
+ export declare const API_META_KEY: unique symbol;
3
+ export declare const METHOD_META_KEY: unique symbol;
4
+ export declare const REQUEST_KEY: unique symbol;
5
+ export declare const RESPONSE_TYPE_KEY: unique symbol;
6
+ export declare const PATH_PARAMS_KEY: unique symbol;
7
+ export declare const SUCCESS_HANDLER_KEY: unique symbol;
8
+ export declare const ERROR_HANDLER_KEY: unique symbol;
9
+ declare const McAxiosAnnotations: {
10
+ GET: (url: string, type: new (res: any) => any) => (target: any, propertyKey: string) => void;
11
+ POST: (url: string, type: new (res: any) => any) => (target: any, propertyKey: string) => void;
12
+ PUT: (url: string, type: new (res: any) => any) => (target: any, propertyKey: string) => void;
13
+ DELETE: (url: string, type: new (res: any) => any) => (target: any, propertyKey: string) => void;
14
+ PATCH: (url: string, type: new (res: any) => any) => (target: any, propertyKey: string) => void;
15
+ Request: (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
16
+ Path: (name: string) => (target: Object, propertyKey: string | symbol, parameterIndex: number) => void;
17
+ Success: (fn: Function) => (target: any, propertyKey: string) => void;
18
+ Error: (fn: Function) => (target: any, propertyKey: string) => void;
19
+ };
20
+ export default McAxiosAnnotations;
@@ -0,0 +1,66 @@
1
+ import "reflect-metadata";
2
+ export const API_META_KEY = Symbol("mc:api");
3
+ export const METHOD_META_KEY = Symbol("mc:method");
4
+ export const REQUEST_KEY = Symbol("mc:requestBody");
5
+ export const RESPONSE_TYPE_KEY = Symbol("mc:responseType");
6
+ export const PATH_PARAMS_KEY = Symbol("mc:pathParams");
7
+ export const SUCCESS_HANDLER_KEY = Symbol("mc:successHandler");
8
+ export const ERROR_HANDLER_KEY = Symbol("mc:errorHandler");
9
+ const McAxiosAnnotations = {
10
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
11
+ GET: (url, type) => {
12
+ return createDecorator("GET", url, type);
13
+ },
14
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
15
+ POST: (url, type) => {
16
+ return createDecorator("POST", url, type);
17
+ },
18
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
19
+ PUT: (url, type) => {
20
+ return createDecorator("PUT", url, type);
21
+ },
22
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
23
+ DELETE: (url, type) => {
24
+ return createDecorator("DELETE", url, type);
25
+ },
26
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
27
+ PATCH: (url, type) => {
28
+ return createDecorator("PATCH", url, type);
29
+ },
30
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
31
+ Request: (target, propertyKey, parameterIndex) => {
32
+ Reflect.defineMetadata(REQUEST_KEY, parameterIndex, target, propertyKey);
33
+ },
34
+ Path: (name) => {
35
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
36
+ return (target, propertyKey, parameterIndex) => {
37
+ const existingParams = Reflect.getMetadata(PATH_PARAMS_KEY, target, propertyKey) || {};
38
+ existingParams[name] = parameterIndex;
39
+ Reflect.defineMetadata(PATH_PARAMS_KEY, existingParams, target, propertyKey);
40
+ };
41
+ },
42
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
43
+ Success: (fn) => {
44
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
45
+ return (target, propertyKey) => {
46
+ Reflect.defineMetadata(SUCCESS_HANDLER_KEY, fn, target, propertyKey);
47
+ };
48
+ },
49
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
50
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
51
+ Error: (fn) => {
52
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
53
+ return (target, propertyKey) => {
54
+ Reflect.defineMetadata(ERROR_HANDLER_KEY, fn, target, propertyKey);
55
+ };
56
+ },
57
+ };
58
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
59
+ const createDecorator = (method, path, type) => {
60
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
61
+ return (target, propertyKey) => {
62
+ Reflect.defineMetadata(METHOD_META_KEY, { method, path }, target, propertyKey);
63
+ Reflect.defineMetadata(RESPONSE_TYPE_KEY, type, target, propertyKey);
64
+ };
65
+ };
66
+ export default McAxiosAnnotations;
@@ -0,0 +1,10 @@
1
+ import type McAxios from "./McAxios";
2
+ export declare const createMcAxios: <T extends McAxios>(api: T) => T;
3
+ export default class McAxiosManager {
4
+ private static _axios;
5
+ private constructor();
6
+ static set(servers: McAxios[]): void;
7
+ static add(axios: McAxios): void;
8
+ static get<T extends McAxios>(type: abstract new (...args: any[]) => T): T;
9
+ static clear(): void;
10
+ }
@@ -0,0 +1,29 @@
1
+ export const createMcAxios = (api) => {
2
+ McAxiosManager.add(api);
3
+ return api;
4
+ };
5
+ export default class McAxiosManager {
6
+ static _axios = [];
7
+ constructor() { }
8
+ static set(servers) {
9
+ McAxiosManager.clear();
10
+ for (const axios of servers)
11
+ McAxiosManager.add(axios);
12
+ }
13
+ static add(axios) {
14
+ McAxiosManager._axios.push(axios);
15
+ }
16
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
17
+ static get(type) {
18
+ const result = McAxiosManager._axios.find((axios) => axios instanceof type);
19
+ if (result)
20
+ return result;
21
+ throw new Error(`Axios is undefined : ${type.name}`);
22
+ }
23
+ // public static delete(type: object) {
24
+ // return McAxiosManager._axios.splice(type);
25
+ // }
26
+ static clear() {
27
+ McAxiosManager._axios = [];
28
+ }
29
+ }
@@ -0,0 +1,13 @@
1
+ import "reflect-metadata";
2
+ declare const McDataAnnotations: {
3
+ Entity: <T extends {
4
+ new (...args: any[]): {};
5
+ }>(constructor: T) => T;
6
+ Serialize: (jsonKey: string) => (target: any, propertyKey: string) => void;
7
+ Field: (type: any, path?: string, defaultValue?: any) => (target: any, propertyKey: string) => void;
8
+ ArrayField: (type: any, path?: string, defaultValue?: any) => (target: any, propertyKey: string) => void;
9
+ };
10
+ export declare abstract class McSerializable {
11
+ toJson(): Record<string, any>;
12
+ }
13
+ export default McDataAnnotations;
@@ -0,0 +1,172 @@
1
+ import "reflect-metadata";
2
+ import McResponse from "./McResponse";
3
+ const ENTITY_FLAG = Symbol("mc:isEntity");
4
+ const SERIALIZE_FLAG = Symbol("mc:serialize");
5
+ const FIELD_TYPE = Symbol("mc:fieldType");
6
+ const FIELD_IS_ARRAY = Symbol("mc:isArray");
7
+ const FIELD_PATH = Symbol("mc:fieldPath");
8
+ const FIELD_DEFAULT = Symbol("mc:fieldDefault");
9
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
10
+ const isEntity = (type) => {
11
+ return !!Reflect.getMetadata(ENTITY_FLAG, type);
12
+ };
13
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
14
+ const findPath = (obj, path) => {
15
+ const keys = path.split(".");
16
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
17
+ let result = obj;
18
+ for (const key of keys) {
19
+ result = result[key];
20
+ }
21
+ return result;
22
+ };
23
+ const McDataAnnotations = {
24
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
25
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
26
+ // biome-ignore lint/complexity/noBannedTypes: <explanation>
27
+ Entity: (constructor) => {
28
+ Reflect.defineMetadata(ENTITY_FLAG, true, constructor);
29
+ return class extends constructor {
30
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
31
+ constructor(...args) {
32
+ super(...args);
33
+ // const response = args[0];
34
+ let body = args[0];
35
+ if (this instanceof McResponse) {
36
+ body = args[0]?.data?.body;
37
+ }
38
+ if (body === undefined)
39
+ return;
40
+ console.log(`Dark Response > ${body.toString()}`);
41
+ const proto = Object.getPrototypeOf(this);
42
+ // 💡 메타데이터가 붙은 키들만 필터링
43
+ const names = Object.getOwnPropertyNames(this).filter((key) => Reflect.hasMetadata(FIELD_TYPE, proto, key));
44
+ for (const key of names) {
45
+ if (typeof key !== "string")
46
+ continue;
47
+ const descriptor = Object.getOwnPropertyDescriptor(proto, key);
48
+ const type = Reflect.getMetadata(FIELD_TYPE, proto, key);
49
+ const isArray = Reflect.getMetadata(FIELD_IS_ARRAY, proto, key);
50
+ const path = Reflect.getMetadata(FIELD_PATH, proto, key);
51
+ const defaultValue = Reflect.getMetadata(FIELD_DEFAULT, proto, key) || {};
52
+ if (!type)
53
+ continue;
54
+ const rawValue = path ? findPath(body, path) : body[key];
55
+ // console.log(`Dartk > ${key.toString()} >> ${isArray} >>> ${Array.isArray(rawValue)}`);
56
+ if (rawValue !== undefined && rawValue !== null) {
57
+ if (isArray && Array.isArray(rawValue)) {
58
+ let cnt = 0;
59
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
60
+ this[key] = rawValue.map((item) => {
61
+ return isEntity(type) ? new type(item) : type(item);
62
+ });
63
+ }
64
+ else if ([String, Number, Boolean].includes(type)) {
65
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
66
+ this[key] = type(rawValue);
67
+ }
68
+ else if (isEntity(type)) {
69
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
70
+ this[key] = new type(rawValue);
71
+ }
72
+ else {
73
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
74
+ this[key] = new type(rawValue);
75
+ }
76
+ }
77
+ else {
78
+ if (isArray && Array.isArray(rawValue)) {
79
+ let cnt = 0;
80
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
81
+ this[key] = rawValue.map((item) => {
82
+ return new type(defaultValue);
83
+ });
84
+ }
85
+ else {
86
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
87
+ this[key] = defaultValue; // 또는 null, 또는 디폴트 값
88
+ }
89
+ }
90
+ }
91
+ // currentPrototype = Object.getPrototypeOf(currentPrototype);
92
+ // }
93
+ }
94
+ };
95
+ },
96
+ Serialize: (jsonKey) => {
97
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
98
+ return (target, propertyKey) => {
99
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
100
+ const properties = Reflect.getMetadata(SERIALIZE_FLAG, target) || [];
101
+ properties.push({
102
+ propertyKey: propertyKey,
103
+ jsonKey: jsonKey,
104
+ });
105
+ Reflect.defineMetadata(SERIALIZE_FLAG, properties, target);
106
+ };
107
+ },
108
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
109
+ Field: (type, path, defaultValue = undefined) => {
110
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
111
+ return (target, propertyKey) => {
112
+ // const type = Reflect.getMetadata("design:type", target, propertyKey);
113
+ Reflect.defineMetadata(FIELD_TYPE, type, target, propertyKey);
114
+ Reflect.defineMetadata(FIELD_DEFAULT, defaultValue, target, propertyKey);
115
+ Reflect.defineMetadata(FIELD_PATH, path, target, propertyKey);
116
+ };
117
+ },
118
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
119
+ ArrayField: (type, path, defaultValue = undefined) => {
120
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
121
+ return (target, propertyKey) => {
122
+ // const type = Reflect.getMetadata("design:type", target, propertyKey);
123
+ Reflect.defineMetadata(FIELD_TYPE, type, target, propertyKey);
124
+ Reflect.defineMetadata(FIELD_IS_ARRAY, true, target, propertyKey);
125
+ Reflect.defineMetadata(FIELD_PATH, path, target, propertyKey);
126
+ };
127
+ },
128
+ // Name: (originalKey: string) => {
129
+ // // biome-ignore lint/suspicious/noExplicitAny: <explanation>
130
+ // return (target: any, propertyKey: string) => {
131
+ // // const type = Reflect.getMetadata("design:type", target, propertyKey);
132
+ // Reflect.defineMetadata(FIELD_NAME, originalKey, target, propertyKey);
133
+ // };
134
+ // },
135
+ };
136
+ export class McSerializable {
137
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
138
+ toJson() {
139
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
140
+ const result = {};
141
+ // 'this'는 런타임에 클래스의 인스턴스를 가리킵니다.
142
+ // 메타데이터는 프로토타입에 저장되어 있으므로 this.constructor.prototype 에서 찾습니다.
143
+ const properties = Reflect.getMetadata(SERIALIZE_FLAG, this.constructor.prototype);
144
+ if (!properties) {
145
+ return result;
146
+ }
147
+ for (const prop of properties) {
148
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
149
+ const value = this[prop.propertyKey];
150
+ // (this as any)를 통해 인스턴스의 프로퍼티 값에 접근합니다.
151
+ if (Array.isArray(value)) {
152
+ // 값이 배열일 경우, 각 항목을 재귀적으로 처리
153
+ result[prop.jsonKey] = value.map((item) => {
154
+ if (item && typeof item.toJson === "function") {
155
+ return item.toJson(); // 배열 안의 객체가 serializable이면 toJson() 호출
156
+ }
157
+ return item; // 아니면 값 그대로 반환
158
+ });
159
+ }
160
+ else if (value && typeof value.toJson === "function") {
161
+ // 값이 단일 serializable 객체일 경우
162
+ result[prop.jsonKey] = value.toJson(); // 해당 객체의 toJson() 호출
163
+ }
164
+ else {
165
+ // 값이 기본 타입이거나 non-serializable 객체일 경우
166
+ result[prop.jsonKey] = value;
167
+ }
168
+ }
169
+ return result;
170
+ }
171
+ }
172
+ export default McDataAnnotations;
@@ -0,0 +1,3 @@
1
+ import { McSerializable } from "./McDataAnnotations";
2
+ export default abstract class McRequest extends McSerializable {
3
+ }
@@ -0,0 +1,3 @@
1
+ import { McSerializable } from "./McDataAnnotations";
2
+ export default class McRequest extends McSerializable {
3
+ }
@@ -0,0 +1,7 @@
1
+ import type { AxiosResponseHeaders, RawAxiosResponseHeaders } from "axios";
2
+ import type { AxiosResponse } from "axios";
3
+ export default abstract class McResponse {
4
+ private readonly _headers;
5
+ protected constructor(response: AxiosResponse);
6
+ get headers(): RawAxiosResponseHeaders | AxiosResponseHeaders;
7
+ }
@@ -0,0 +1,9 @@
1
+ export default class McResponse {
2
+ _headers;
3
+ constructor(response) {
4
+ this._headers = response.headers;
5
+ }
6
+ get headers() {
7
+ return this._headers;
8
+ }
9
+ }
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "mcl-axios-kit",
3
+ "version": "0.0.1",
4
+ "description": "McL에서 Axios를 활용해 API를 보다 효율적이고 깔끔하게 활용할 수 있도록 구현된 Kit",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "rm -rf dist && tsc",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "axios", "typescript", "mcl"
23
+ ],
24
+ "author": "IDEa Team",
25
+ "license": "Apache-2.0",
26
+ "dependencies": {
27
+ "reflect-metadata": "^0.2.2"
28
+ },
29
+ "peerDependencies": {
30
+ "axios": "^1.13.4"
31
+ },
32
+ "devDependencies": {
33
+ "axios": "^1.13.4",
34
+ "@types/node": "^25.2.0",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "registry": "https://registry.npmjs.org"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/dark1004-k/mcl-axios-kit.git"
44
+ }
45
+ }