eve-esi-types 2.0.4-beta → 2.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.
package/v2.mts DELETED
@@ -1,320 +0,0 @@
1
- /*!
2
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3
- Copyright (C) 2025 jeffy-g <hirotom1107@gmail.com>
4
- Released under the MIT license
5
- https://opensource.org/licenses/mit-license.php
6
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7
- */
8
- // - - - - - - - - - - - - - - - - - - - -
9
- // imports
10
- // - - - - - - - - - - - - - - - - - - - -
11
- import type { TESIResponseOKMap } from "./v2";
12
- // import type { TESIResponseOKMap } from "eve-esi-types";
13
-
14
-
15
- // - - - - - - - - - - - - - - - - - - - -
16
- // constants, types
17
- // - - - - - - - - - - - - - - - - - - - -
18
- // shorthands
19
- const log = console.log;
20
- const isArray = Array.isArray;
21
- /**
22
- * enable/disable console.log
23
- */
24
- let LOG = false;
25
- /**
26
- * this always `https://esi.evetech.net`
27
- */
28
- const BASE = "https://esi.evetech.net";
29
-
30
-
31
- type ESIRequestOptions = {
32
- /**
33
- * query params for ESI request.
34
- */
35
- queries?: Record<string, any>;
36
- /**
37
- * will need it for `POST` request etc.
38
- */
39
- body?: any;
40
- /**
41
- * if want response data with ignore error then can be set to `true`.
42
- */
43
- ignoreError?: boolean;
44
- /**
45
- * cancel request immediately
46
- */
47
- cancelable?: AbortController;
48
-
49
- /**
50
- * Can be an empty object if no authentication is required.description
51
- */
52
- token?: string;
53
-
54
- auth?: true;
55
- };
56
- /**
57
- * @typedef {import("./v2").TESIResponseOKMap} TESIResponseOKMap
58
- */
59
- /**
60
- * @typedef ESIRequestOptions
61
- * @prop {Record<string, any>} [queries] query params for ESI request.
62
- * @prop {any} [body] will need it for `POST` request etc.
63
- * @prop {boolean} [ignoreError] if want response data with ignore error then can be set to `true`.
64
- * @prop {AbortController} [cancelable] cancel request immediately
65
- * @prop {string} [token] Can be an empty object if no authentication is required.description
66
- * @prop {true=} [auth] Can be an empty object if no authentication is required.description
67
- */
68
-
69
-
70
- // - - - - - - - - - - - - - - - - - - - -
71
- // module vars, functions
72
- // - - - - - - - - - - - - - - - - - - - -
73
- /**
74
- * Get the number of currently executing ESI requests
75
- */
76
- let ax: number = 0;
77
- /**
78
- * simple named error class.
79
- */
80
- class ESIRequesError extends Error {}
81
- /**
82
- * throws when x-esi-error-limit-remain header value is "0". (http status: 420)
83
- */
84
- class ESIErrorLimitReachedError extends Error {
85
- constructor() {
86
- super("Cannot continue ESI request because 'x-esi-error-limit-remain' is zero!");
87
- }
88
- valueOf(): number {
89
- return 420;
90
- }
91
- }
92
- /**
93
- * fetch the extra pages
94
- *
95
- * + if the `x-pages` header property ware more than 1
96
- *
97
- * @param {string} endpointUrl
98
- * @param {RequestInit} rqopt request options
99
- * @param {Record<string, any>} qs queries
100
- * @param {number} pc pageCount
101
- */
102
- const fetchP = async <T extends any>(endpointUrl: string, rqopt: RequestInit, qs: Record<string, any>, pc: number) => {
103
- const rqs: Promise<T>[] = [];
104
- const rqp = new URLSearchParams(qs);
105
- for (let i = 2; i <= pc; ) {
106
- rqp.set("page", (i++) + "");
107
- ax++;
108
- rqs.push(
109
- fetch(`${endpointUrl}?${rqp + ""}`, rqopt).then(
110
- res => res.json()
111
- ).catch(reason => {
112
- console.warn(reason);
113
- return [] as T;
114
- }).finally(() => {
115
- ax--;
116
- })
117
- );
118
- }
119
- return Promise.all(rqs).then(jsons => {
120
- // DEVNOTE: let check the page 2, type is array?
121
- if (isArray(jsons[0])) {
122
- let combined: any[] = [];
123
- for (let i = 0, end = jsons.length; i < end;) {
124
- combined = combined.concat(jsons[i++]);
125
- }
126
- return combined as T;
127
- }
128
-
129
- LOG && log("> > > pages result are object < < < --", jsons);
130
- return null;
131
- });
132
- };
133
-
134
- /** ### replace (C)urly (B)races (T)oken
135
- *
136
- * @example
137
- * "/characters/{character_id}/skills"
138
- * // ->
139
- * "/characters/<char.character_id>/skills"
140
- *
141
- * @param {string} endpoint e.g - "/characters/{character_id}/"
142
- * @param {number[]} ids
143
- * @returns fragment of qualified endpoint uri or null.
144
- */
145
- const replaceCbt = (endpoint: string, ids: number[]) => {
146
- const re = /{([\w]+)}/g;
147
- /** @type {RegExpExecArray?} */
148
- let m: RegExpExecArray | null;
149
- let idx = 0
150
- while (m = re.exec(endpoint)) {
151
- endpoint = endpoint.replace(m[0], ids[idx++] + "");
152
- }
153
- return endpoint;
154
- };
155
- /**
156
- *
157
- * @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
158
- * + The version parameter can be omitted by using `<version>/<endpoint>`
159
- */
160
- const curl = (endp: string) => {
161
- endp = endp.replace(/^\/+|\/+$/g, "");
162
- return `${BASE}/latest/${endp}/`;
163
- };
164
-
165
-
166
- // - - - - - - - - - - - - - - - - - - - -
167
- // main functions
168
- // - - - - - - - - - - - - - - - - - - - -
169
- // It should complete correctly.
170
- async function getEVEStatus() {
171
- try {
172
- const ok = await fire("get", "/characters/{character_id}/ship/", 994562, { auth: true, token: "<accessToken of 994562>" });
173
- console.log(ok);
174
- } catch (error) {
175
- console.error("Failed to get character ship -", error);
176
- }
177
- return fire("get", "/status/");
178
- }
179
-
180
- /**
181
- * fire ESI request
182
- * @template {TESIEntryMethod} M
183
- * @template {keyof TESIResponseOKMap[M]} EP
184
- * @template {IdentifyParameters<TESIResponseOKMap[M][EP], ESIRequestOptions>} Opt
185
- * @template {InferESIResponseResult<M, EP>} R
186
- *
187
- * @param {M} mthd
188
- * @param {EP} endp - The endpoint to request.
189
- * @param {number | number[] | Opt} [pathParams] - Optional path parameters.
190
- * @param {Opt} [opt] - default is empty object {}. `body` is json string
191
- * @returns {Promise<R>} - The response from the endpoint.
192
- * @throws
193
- * @async
194
- */
195
- export async function fire<
196
- M extends TESIEntryMethod,
197
- EP extends keyof TESIResponseOKMap[M],
198
- Opt extends IdentifyParameters<TESIResponseOKMap[M][EP], ESIRequestOptions>,
199
- R extends InferESIResponseResult<M, EP>
200
- >(
201
- mthd: M, endp: EP, pathParams?: number | number[] | Opt,
202
- opt: Opt = {} as Opt
203
- ): Promise<R> {
204
-
205
- if (typeof pathParams === "number") {
206
- pathParams = /** @type {number[]} */([pathParams]);
207
- }
208
- if (isArray(pathParams)) {
209
- // @ts-ignore actualy endp is string
210
- endp = replaceCbt(endp as string, pathParams) as EP;
211
- }
212
- // When only options are provided
213
- // @ts- ignore
214
- opt = /** @type {Opt} */(pathParams) as Opt || opt || /** @type {Opt} */({}) as Opt;
215
-
216
- /** @type {RequestInit} */
217
- const rqopt: RequestInit = {
218
- method: mthd,
219
- mode: "cors",
220
- cache: "no-cache",
221
- // @ts-ignore
222
- signal: opt.cancelable?.signal,
223
- headers: {}
224
- };
225
- const qss: Record<string, string> = {
226
- language: "en",
227
- };
228
-
229
- if (opt.queries) {
230
- // Object.assign(queries, options.queries); Object.assign is too slow
231
- const oqs = opt.queries;
232
- for (const k of Object.keys(oqs)) {
233
- qss[k] = oqs[k] as string;
234
- }
235
- }
236
- // DEVNOTE: when datasource is not empty string. (e.g - "singularity"
237
- // in this case must specify datasource.
238
- // disabled since `REMOVING DATASOURCE SINGULARITY`
239
- // if (opt.datasource === "singularity") {
240
- // opt.datasource = "tranquility";
241
- // }
242
- if (opt.auth) {
243
- // @ts-ignore The header is indeed an object
244
- (rqopt.headers as any).authorization = `Bearer ${opt.token}`;
245
- }
246
- if (opt.body) { // means "POST" method etc
247
- // @ts-ignore The header is indeed an object
248
- (rqopt.headers as any)["content-type"] = "application/json";
249
- rqopt.body = JSON.stringify(opt.body);
250
- }
251
-
252
- // @ts-ignore actualy endp is string
253
- const endpointUrl = curl(endp);
254
- ax++;
255
- try {
256
- // @ts-ignore A silly type error will appear, but ignore it.
257
- const res = await fetch(
258
- `${endpointUrl}?${new URLSearchParams(qss) + ""}`, rqopt
259
- ).finally(() => {
260
- ax--;
261
- });
262
-
263
- const stat = res.status;
264
- if (!res.ok && !opt.ignoreError) {
265
- if (stat === 420) {
266
- opt.cancelable && opt.cancelable.abort();
267
- throw new ESIErrorLimitReachedError();
268
- } else {
269
- // console.log(res);
270
- throw new ESIRequesError(`${res.statusText} (status=${stat})`);
271
- }
272
- } else {
273
- // DEVNOTE: - 204 No Content
274
- if (stat === 204) {
275
- // this result is empty, decided to return status code.
276
- return /** @type {R} */({ status: stat }) as unknown as R;
277
- }
278
-
279
- /** @type {R} */
280
- const data: R = await res.json();
281
- if (opt.ignoreError) {
282
- // meaning `forceJson`?
283
- return data;
284
- }
285
- // - - - - x-pages response.
286
- // +undefined is NaN
287
- // @ts-ignore becouse +null is 0
288
- const pc = +res.headers.get("x-pages")!;
289
- // has remaining pages? NaN > 1 === false !isNaN(pageCount)
290
- if (pc > 1) {
291
- LOG && log('found "x-pages" header, pages: %d', pc);
292
- const remData = await fetchP<R>(endpointUrl, rqopt, qss, pc);
293
- // finally, decide product data.
294
- if (isArray(data) && isArray(remData)) {
295
- // DEVNOTE: 2019/7/23 15:01:48 - types
296
- return /** @type {R} */(data.concat(remData)) as unknown as R;
297
- } else {
298
- // @ts-ignore TODO: fix type
299
- remData && Object.assign(data, remData);
300
- }
301
- }
302
-
303
- return data;
304
- }
305
-
306
- } catch (e) {
307
- // @ts-ignore actualy endp is string
308
- throw new ESIRequesError(`message: ${(e as Error).message}, endpoint=${endp}`);
309
- }
310
- }
311
-
312
- // type following and run
313
- // node v2.mjs
314
- // or yarn test:v2
315
- getEVEStatus().then(eveStatus => console.log(eveStatus));
316
- // {
317
- // "players": 16503,
318
- // "server_version": "2794925",
319
- // "start_time": "2025-01-21T11:02:34Z"
320
- // }