dnf-api 1.0.6 → 1.1.0

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.
Files changed (66) hide show
  1. package/dist/api/auction.d.ts +24 -0
  2. package/dist/api/characters.d.ts +32 -0
  3. package/dist/api/characters.equip.d.ts +29 -0
  4. package/dist/api/characters.skill.d.ts +29 -0
  5. package/dist/api/index.d.ts +9 -0
  6. package/dist/api/items.d.ts +21 -0
  7. package/dist/api/multi.d.ts +7 -0
  8. package/dist/api/server.d.ts +2 -0
  9. package/dist/api/setitems.d.ts +15 -0
  10. package/dist/index.d.ts +21 -0
  11. package/dist/index.js +19 -19
  12. package/dist/model/character.d.ts +91 -0
  13. package/dist/{src/model/auction.d.ts → model/index.d.ts} +38 -0
  14. package/dist/model/item.d.ts +103 -0
  15. package/dist/model/setitem.d.ts +32 -0
  16. package/dist/src/api/auction.d.ts +24 -24
  17. package/dist/src/api/characters.d.ts +32 -32
  18. package/dist/src/api/characters.equip.d.ts +29 -29
  19. package/dist/src/api/characters.skill.d.ts +29 -29
  20. package/dist/src/api/index.d.ts +9 -9
  21. package/dist/src/api/items.d.ts +21 -21
  22. package/dist/src/api/multi.d.ts +7 -7
  23. package/dist/src/api/server.d.ts +2 -2
  24. package/dist/src/api/setitems.d.ts +15 -15
  25. package/dist/src/index.d.ts +21 -14
  26. package/dist/src/model/character.d.ts +91 -91
  27. package/dist/src/model/index.d.ts +85 -39
  28. package/dist/src/model/item.d.ts +103 -103
  29. package/dist/src/model/setitem.d.ts +32 -32
  30. package/dist/src/util/config.d.ts +11 -11
  31. package/dist/src/util/index.d.ts +5 -5
  32. package/dist/src/util/params.d.ts +76 -76
  33. package/dist/src/util/query.d.ts +14 -14
  34. package/dist/src/util/static.d.ts +50 -50
  35. package/dist/util/config.d.ts +21 -0
  36. package/dist/util/index.d.ts +7 -0
  37. package/dist/util/params.d.ts +80 -0
  38. package/dist/util/query.d.ts +39 -0
  39. package/dist/util/queue.d.ts +31 -0
  40. package/dist/util/request-helper.d.ts +9 -0
  41. package/dist/util/static.d.ts +50 -0
  42. package/package.json +42 -39
  43. package/src/api/auction.ts +67 -67
  44. package/src/api/characters.equip.ts +81 -81
  45. package/src/api/characters.skill.ts +86 -86
  46. package/src/api/characters.ts +91 -91
  47. package/src/api/index.ts +10 -10
  48. package/src/api/items.ts +49 -49
  49. package/src/api/multi.ts +17 -17
  50. package/src/api/server.ts +9 -9
  51. package/src/api/setitems.ts +29 -29
  52. package/src/index.ts +35 -28
  53. package/src/model/character.ts +98 -98
  54. package/src/model/index.ts +95 -47
  55. package/src/model/item.ts +117 -117
  56. package/src/model/setitem.ts +33 -33
  57. package/src/util/config.ts +43 -20
  58. package/src/util/index.ts +17 -6
  59. package/src/util/params.ts +95 -82
  60. package/src/util/query.ts +144 -93
  61. package/src/util/queue.ts +104 -0
  62. package/src/util/request-helper.ts +19 -0
  63. package/src/util/static.ts +52 -52
  64. package/bun.lockb +0 -0
  65. package/dist/test.d.ts +0 -1
  66. package/src/model/auction.ts +0 -48
package/src/util/query.ts CHANGED
@@ -1,93 +1,144 @@
1
- import consola from "consola";
2
- import querystring from "query-string";
3
- import { request } from "undici";
4
-
5
- import type * as model from "../model";
6
- import * as Util from "./";
7
-
8
- const apiUrl = new URL("https://api.neople.co.kr");
9
- // const client = new Client("https://api.neople.co.kr", {
10
- // connectTimeout: Util.Config.timeout,
11
- // // allowH2: true,
12
- // });
13
-
14
- const sender = async <T>(path: string, method: "GET" | "POST", query: any) => {
15
- apiUrl.pathname = path;
16
- apiUrl.search = querystring.stringify(query);
17
- const res = await request<model.IDnfResponse<T>>(apiUrl.href, {
18
- method,
19
- });
20
- return res;
21
- };
22
-
23
- const showUrl = (url: string): string => {
24
- if (Util.config.key) {
25
- return url?.replace(Util.config.key, Util.config.hideKeyText);
26
- } else {
27
- return url;
28
- }
29
- };
30
-
31
- // biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
32
- export default class Request {
33
- public static UriBuilder(...args: any[]): string {
34
- return args.join("/");
35
- }
36
- public static QueryBuilder(query: string[] | number[]): string {
37
- const qString: string[] = [];
38
- for (const key in query) {
39
- qString.push(`${key}:${query[key]},`);
40
- }
41
- return qString.join(",");
42
- }
43
-
44
- /**
45
- * 던전앤파이터 API 서버에 응답을 요청하는 함수 입니다.
46
- * 해당 함수를 직접 호출 하는것을 권장하지 않습니다.
47
- *
48
- * @param {object} opt (요청을 보낼 Parameter값)
49
- * @returns
50
- */
51
- public static async Request<T>(
52
- opt: any = {},
53
- method: "GET" | "POST" = "GET"
54
- ): Promise<model.IDnfResponse<T>> {
55
- if (!Util.config.key || Util.config.key === "") {
56
- consola.error("Please change to your api key. ");
57
- }
58
-
59
- if (opt.params === undefined) opt.params = {};
60
- if (opt.params.q) opt.params.q = Request.QueryBuilder(opt.params.q);
61
-
62
- opt.params.apikey = Util.config.key;
63
-
64
- if (Util.config.showURL)
65
- consola.log(
66
- "request url:",
67
- showUrl(`${opt.base}?${querystring.stringify(opt.params)}`)
68
- );
69
-
70
- const res = await sender<T>(opt.base, method, opt.params);
71
- if (res.statusCode !== 200) {
72
- const resBody = (await res.body.json()) as model.IDnfResponse<T>;
73
- const error: model.IDnfErrorResponse = {
74
- url: showUrl(opt.url ?? ""),
75
- status: res.statusCode || 0,
76
- statusText: "",
77
- code: resBody.error?.code || "",
78
- message: resBody.error?.message || "",
79
- };
80
- return { error };
81
- } else {
82
- const resBody = (await res.body.json()) as T;
83
- return {
84
- data: resBody,
85
- };
86
- }
87
- }
88
-
89
- public static makeItemQuery(query: any) {
90
- // return JSON.stringify(query).replace(/\"|\{|\}/gi, "");
91
- return encodeURI(query);
92
- }
93
- }
1
+ import consola from "consola";
2
+
3
+ import type * as model from "../model";
4
+ import * as Util from "./";
5
+ import type { BaseParams } from "./params";
6
+ import { requestQueue } from "./queue";
7
+
8
+ // 요청 옵션 인터페이스
9
+ export interface RequestOptions {
10
+ base: string;
11
+ params?: BaseParams;
12
+ url?: string;
13
+ }
14
+
15
+ const API_BASE_URL = "https://api.neople.co.kr";
16
+
17
+ // 파라미터를 URLSearchParams로 변환 (중첩 객체는 JSON으로 직렬화)
18
+ const toSearchParams = (params: BaseParams): URLSearchParams => {
19
+ const entries: [string, string][] = [];
20
+ for (const [key, value] of Object.entries(params)) {
21
+ if (value === undefined) continue;
22
+ if (typeof value === "object" && !Array.isArray(value)) {
23
+ entries.push([key, JSON.stringify(value)]);
24
+ } else if (Array.isArray(value)) {
25
+ entries.push([key, value.join(",")]);
26
+ } else {
27
+ entries.push([key, String(value)]);
28
+ }
29
+ }
30
+ return new URLSearchParams(entries);
31
+ };
32
+
33
+ // HTTP 요청 전송 함수
34
+ const sender = async <T>(
35
+ path: string,
36
+ method: "GET" | "POST",
37
+ query: BaseParams,
38
+ ): Promise<{
39
+ ok: boolean;
40
+ status: number;
41
+ data: T | model.IDnfResponse<T>;
42
+ }> => {
43
+ const url = new URL(path, API_BASE_URL);
44
+ url.search = toSearchParams(query).toString();
45
+
46
+ const res = await fetch(url.toString(), { method });
47
+ const data = (await res.json()) as T | model.IDnfResponse<T>;
48
+
49
+ return {
50
+ ok: res.ok,
51
+ status: res.status,
52
+ data,
53
+ };
54
+ };
55
+
56
+ // URL에서 API 숨김 처리
57
+ const showUrl = (url: string): string => {
58
+ if (Util.config.key) {
59
+ return url?.replace(Util.config.key, Util.config.hideKeyText);
60
+ }
61
+ return url;
62
+ };
63
+
64
+ /**
65
+ * URI 경로를 빌드합니다.
66
+ * @param args 경로 세그먼트들
67
+ * @returns 결합된 URI 경로
68
+ */
69
+ export function UriBuilder(...args: (string | number)[]): string {
70
+ return args.join("/");
71
+ }
72
+
73
+ /**
74
+ * 쿼리 문자열을 빌드합니다.
75
+ * @param query 쿼리 배열
76
+ * @returns 쿼리 문자열
77
+ */
78
+ export function QueryBuilder(query: (string | number)[]): string {
79
+ const qString: string[] = [];
80
+ for (const key in query) {
81
+ qString.push(`${key}:${query[key]},`);
82
+ }
83
+ return qString.join(",");
84
+ }
85
+
86
+ /**
87
+ * 던전앤파이터 API 서버에 요청을 보내는 함수입니다.
88
+ * @param opt 요청 옵션
89
+ * @param method HTTP 메서드
90
+ * @returns API 응답
91
+ */
92
+ export async function Request<T>(
93
+ opt: RequestOptions = { base: "" },
94
+ method: "GET" | "POST" = "GET",
95
+ ): Promise<model.IDnfResponse<T>> {
96
+ if (!Util.config.key) {
97
+ throw new Error(
98
+ "API key is required. Set config.key before making requests.",
99
+ );
100
+ }
101
+
102
+ const params: BaseParams = opt.params ?? {};
103
+
104
+ if (params.q && Array.isArray(params.q)) {
105
+ params.q = QueryBuilder(params.q as (string | number)[]);
106
+ }
107
+
108
+ params.apikey = Util.config.key;
109
+
110
+ if (Util.config.showURL) {
111
+ consola.log(
112
+ "request url:",
113
+ showUrl(`${opt.base}?${toSearchParams(params)}`),
114
+ );
115
+ }
116
+
117
+ const res = await requestQueue.add(() => sender<T>(opt.base, method, params));
118
+
119
+ if (!res.ok) {
120
+ const resBody = res.data as model.IDnfResponse<T>;
121
+ const error: model.IDnfErrorResponse = {
122
+ url: showUrl(opt.url ?? ""),
123
+ status: res.status || 0,
124
+ statusText: "",
125
+ code: resBody.error?.code || "",
126
+ message: resBody.error?.message || "",
127
+ };
128
+ return { error };
129
+ }
130
+
131
+ return { data: res.data as T };
132
+ }
133
+
134
+ /**
135
+ * 아이템 쿼리를 생성합니다.
136
+ * @param query 쿼리 문자열
137
+ * @returns 인코딩된 쿼리
138
+ */
139
+ export function makeItemQuery(query: string): string {
140
+ return encodeURI(query);
141
+ }
142
+
143
+ // 기존 호환성을 위한 default export
144
+ export default { UriBuilder, QueryBuilder, Request, makeItemQuery };
@@ -0,0 +1,104 @@
1
+ import config from "./config";
2
+
3
+ /**
4
+ * Rate limiting 요청 큐
5
+ * 1초당 최대 요청 수를 제한합니다.
6
+ */
7
+
8
+ interface QueueItem<T> {
9
+ task: () => Promise<T>;
10
+ resolve: (value: T) => void;
11
+ reject: (error: Error) => void;
12
+ }
13
+
14
+ export class RequestQueue {
15
+ private queue: QueueItem<unknown>[] = [];
16
+ private processing = false;
17
+ private requestCount = 0;
18
+ private lastResetTime = Date.now();
19
+
20
+ /**
21
+ * 큐에 요청을 추가합니다.
22
+ * @param task 실행할 비동기 작업
23
+ * @returns 작업 결과 Promise
24
+ */
25
+ async add<T>(task: () => Promise<T>): Promise<T> {
26
+ return new Promise<T>((resolve, reject) => {
27
+ this.queue.push({
28
+ task,
29
+ resolve: resolve as (value: unknown) => void,
30
+ reject,
31
+ });
32
+ this.processQueue();
33
+ });
34
+ }
35
+
36
+ /**
37
+ * 큐를 처리합니다.
38
+ */
39
+ private async processQueue(): Promise<void> {
40
+ if (this.processing) return;
41
+ this.processing = true;
42
+
43
+ while (this.queue.length > 0) {
44
+ // 1초마다 카운터 리셋
45
+ const now = Date.now();
46
+ if (now - this.lastResetTime >= 1000) {
47
+ this.requestCount = 0;
48
+ this.lastResetTime = now;
49
+ }
50
+
51
+ // 제한에 도달하면 대기 (config에서 실시간으로 가져옴)
52
+ if (this.requestCount >= config.maxRequestsPerSecond) {
53
+ const waitTime = 1000 - (now - this.lastResetTime);
54
+ if (waitTime > 0) {
55
+ await this.sleep(waitTime);
56
+ }
57
+ this.requestCount = 0;
58
+ this.lastResetTime = Date.now();
59
+ }
60
+
61
+ const item = this.queue.shift();
62
+ if (!item) break;
63
+
64
+ this.requestCount++;
65
+
66
+ try {
67
+ const result = await item.task();
68
+ item.resolve(result);
69
+ } catch (error) {
70
+ item.reject(error as Error);
71
+ }
72
+ }
73
+
74
+ this.processing = false;
75
+ }
76
+
77
+ private sleep(ms: number): Promise<void> {
78
+ return new Promise((resolve) => setTimeout(resolve, ms));
79
+ }
80
+
81
+ /**
82
+ * 현재 큐 상태를 반환합니다.
83
+ */
84
+ get status() {
85
+ return {
86
+ queueLength: this.queue.length,
87
+ requestCount: this.requestCount,
88
+ maxRequestsPerSecond: config.maxRequestsPerSecond,
89
+ };
90
+ }
91
+
92
+ /**
93
+ * 큐를 초기화합니다.
94
+ */
95
+ clear(): void {
96
+ this.queue = [];
97
+ this.requestCount = 0;
98
+ }
99
+ }
100
+
101
+ // 기본 큐 인스턴스 (제한 수치는 config를 따름)
102
+ export const requestQueue = new RequestQueue();
103
+
104
+ export default requestQueue;
@@ -0,0 +1,19 @@
1
+ import type * as model from "../model";
2
+ import type { BaseParams } from "./params";
3
+ import { Request, UriBuilder } from "./query";
4
+
5
+ /**
6
+ * API 요청을 생성하는 헬퍼 함수
7
+ * @param baseParts URI 경로 세그먼트들
8
+ * @param params 요청 파라미터
9
+ * @returns API 응답 Promise
10
+ */
11
+ export function createRequest<T>(
12
+ baseParts: (string | number)[],
13
+ params?: BaseParams,
14
+ ): Promise<model.IDnfResponse<T>> {
15
+ return Request<T>({
16
+ base: UriBuilder(...baseParts),
17
+ params,
18
+ });
19
+ }
@@ -1,52 +1,52 @@
1
- export enum server {
2
- Cain = "cain",
3
- Diregie = "diregie",
4
- Siroco = "siroco",
5
- Prey = "prey",
6
- Casillas = "casillas",
7
- Hilder = "hilder",
8
- Anton = "anton",
9
- Bakal = "bakal",
10
- }
11
- export enum sort {
12
- Asc = "asc",
13
- Desc = "desc",
14
- }
15
- export enum rarity {
16
- Common = "커먼",
17
- Uncommon = "언커먼",
18
- Rare = "레어",
19
- Unique = "유니크",
20
- Eqic = "에픽",
21
- Chronicle = "크로니클",
22
- Legendary = "레전더리",
23
- }
24
-
25
- export enum auctionWordType {
26
- Match = "match",
27
- Front = "front",
28
- Full = "full",
29
- }
30
- export enum wordType {
31
- Match = "match",
32
- Front = "front",
33
- Full = "full",
34
- }
35
- export enum charactersWordType {
36
- Match = "match",
37
- Full = "full",
38
- }
39
- export enum baseUri {
40
- Servers = "df/servers",
41
- Auction = "df/auction",
42
- AuctionSold = "df/auction-sold",
43
- Item = "df/items",
44
- SetItem = "df/setitems",
45
- Multi = "df/multi",
46
- }
47
-
48
- export enum reinforceType {
49
- reinforce = "강화",
50
- minRefine = "증폭",
51
- modify = "개조",
52
- }
1
+ export enum server {
2
+ Cain = "cain",
3
+ Diregie = "diregie",
4
+ Siroco = "siroco",
5
+ Prey = "prey",
6
+ Casillas = "casillas",
7
+ Hilder = "hilder",
8
+ Anton = "anton",
9
+ Bakal = "bakal",
10
+ }
11
+ export enum sort {
12
+ Asc = "asc",
13
+ Desc = "desc",
14
+ }
15
+ export enum rarity {
16
+ Common = "커먼",
17
+ Uncommon = "언커먼",
18
+ Rare = "레어",
19
+ Unique = "유니크",
20
+ Eqic = "에픽",
21
+ Chronicle = "크로니클",
22
+ Legendary = "레전더리",
23
+ }
24
+
25
+ export enum auctionWordType {
26
+ Match = "match",
27
+ Front = "front",
28
+ Full = "full",
29
+ }
30
+ export enum wordType {
31
+ Match = "match",
32
+ Front = "front",
33
+ Full = "full",
34
+ }
35
+ export enum charactersWordType {
36
+ Match = "match",
37
+ Full = "full",
38
+ }
39
+ export enum baseUri {
40
+ Servers = "df/servers",
41
+ Auction = "df/auction",
42
+ AuctionSold = "df/auction-sold",
43
+ Item = "df/items",
44
+ SetItem = "df/setitems",
45
+ Multi = "df/multi",
46
+ }
47
+
48
+ export enum reinforceType {
49
+ reinforce = "강화",
50
+ minRefine = "증폭",
51
+ modify = "개조",
52
+ }
package/bun.lockb DELETED
Binary file
package/dist/test.d.ts DELETED
@@ -1 +0,0 @@
1
- import "dotenv/config";
@@ -1,48 +0,0 @@
1
- /** 경매장 아이템 인터페이스 */
2
- export interface IAuction {
3
- auctionNo: number;
4
- regDate: Date;
5
- expireDate: Date;
6
- itemId: string;
7
- itemName: string;
8
- itemAvailableLevel: number;
9
- itemRarity: string;
10
- itemTypeId: string;
11
- itemType: string;
12
- itemTypeDetailId: string;
13
- itemTypeDetail: string;
14
- refine: number;
15
- reinforce: number;
16
- amplificationName: string;
17
- fame: number;
18
- count: number;
19
- regCount: number;
20
- price: number;
21
- currentPrice: number;
22
- unitPrice: number;
23
- averagePrice: number;
24
- upgrade?: number;
25
- upgradeMax?: number;
26
- }
27
-
28
- /** 판매 완료 아이템 인터페이스 */
29
- export interface IAuctionSolid {
30
- soldDate: string;
31
- itemId: string;
32
- itemName: string;
33
- itemAvailableLevel: number;
34
- itemRarity: string;
35
- itemTypeId: string;
36
- itemType: string;
37
- itemTypeDetailId: string;
38
- itemTypeDetail: string;
39
- refine: number;
40
- reinforce: number;
41
- amplificationName: string | null;
42
- fame: number;
43
- count: number;
44
- price: number;
45
- unitPrice: number;
46
- upgrade?: number;
47
- upgradeMax?: number;
48
- }