eve-esi-types 2.3.0 → 2.3.3

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/esi-types-util.md CHANGED
@@ -28,7 +28,11 @@ type TESIRequestFunctionSignature<ActualOpt> = <
28
28
  `IfParameterizedPath<EP, Opt>` if parameterized path then specify number type, otherwise will be `Opt` type.
29
29
 
30
30
  ```ts
31
- type IfParameterizedPath<EP, Opt> = EP extends `${string}/{${string}}/${string | ""}` ? number | number[]: Opt;
31
+ type IfParameterizedPath<EP, Opt> = EP extends `${string}/{${string}}${string}`
32
+ ? PickPathParameters<EP> extends never
33
+ ? Opt : InferKeysLen<PickPathParameters<EP>> extends 1
34
+ ? number : [number, number]
35
+ : Opt;
32
36
  ```
33
37
 
34
38
  ### IdentifyParameters
package/index.html ADDED
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>eve-esi-types ESI request test</title>
7
+ <script type="module" src="./minimal-rq.mjs"></script>
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ </div>
12
+ <div class="info">
13
+ browser-sync start -s --port 8080 --no-open --ss ./<br>
14
+ then access to <a href="http://localhost:8080/?x=1&debug=1">eve-esi-types ESI request test</a>
15
+ </div>
16
+ <footer class="footer"></footer>
17
+ </body>
18
+ </html>
@@ -0,0 +1,104 @@
1
+
2
+ type TEVEErrorBase = {
3
+ /**
4
+ * error message
5
+ */
6
+ error: string;
7
+ };
8
+ /**
9
+ * There are endpoints with status 200 only that contain the "X-Pages" header.
10
+ */
11
+ export type TSuccessStats = 200 | 201 | 204;
12
+ export type TRedirectStats = 304;
13
+ export type TClientErrorStats = 400 | 401 | 403 | 404 | 420 | 422;
14
+ export type TServerErrorStats = 500 | 503 | 504 | 520;
15
+ export type TESIErrorStats = TClientErrorStats | TServerErrorStats;
16
+ export type TStatsAll = TSuccessStats | TRedirectStats | TClientErrorStats | TServerErrorStats;
17
+ export type TESIErrorStatMap = {
18
+ // status 400
19
+ 400: BadRequest;
20
+ 401: Unauthorized;
21
+ 403: Forbidden;
22
+ 404: NotFound;
23
+ 420: ErrorLimited;
24
+ 422: Unprocessable;
25
+
26
+ // status 500
27
+ 500: InternalServerError;
28
+ 503: ServiceUnavailable;
29
+ 504: GatewayTimeout;
30
+ 520: EVEServerError;
31
+ };
32
+ /**
33
+ * ```ts
34
+ * const res = await fetch("<URL with ESI endpoint>");
35
+ * const status = res.status;
36
+ * if (status >= 400) {
37
+ * const errorType: TESIErrorWithStat<typeof status> = {
38
+ * status, ...res.json()
39
+ * };
40
+ * console.log(errorType);
41
+ * throw new Error(`message="${res.statusText}", status=${status}`);
42
+ * }
43
+ * ```
44
+ *
45
+ * @date 2025/3/3
46
+ * @since 2.4.0
47
+ */
48
+ export type TESIErrorWithStat<Stat extends TClientErrorStats | TServerErrorStats> = {
49
+ status: Stat;
50
+ } & TESIErrorStatMap[Stat];
51
+ // declare const bad: TESIErrorWithStat<400>;
52
+
53
+ /**
54
+ * Bad request model 400
55
+ */
56
+ export interface BadRequest extends TEVEErrorBase {}
57
+ /**
58
+ * Not Found model 404
59
+ */
60
+ export interface NotFound extends TEVEErrorBase {}
61
+ /**
62
+ * Unprocessable model 422[Unprocessable entity]
63
+ */
64
+ export interface Unprocessable extends TEVEErrorBase {}
65
+ /**
66
+ * Unauthorized model 401
67
+ */
68
+ export interface Unauthorized extends TEVEErrorBase {}
69
+ /**
70
+ * Forbidden model 403
71
+ */
72
+ export interface Forbidden extends TEVEErrorBase {
73
+ /**
74
+ * status code received from SSO
75
+ */
76
+ sso_status?: number;
77
+ }
78
+
79
+ /**
80
+ * Error limited model 420
81
+ */
82
+ export interface ErrorLimited extends TEVEErrorBase {}
83
+
84
+ /**
85
+ * Internal server error model 500
86
+ */
87
+ export interface InternalServerError extends TEVEErrorBase {}
88
+ /**
89
+ * Service unavailable model 503
90
+ */
91
+ export interface ServiceUnavailable extends TEVEErrorBase {}
92
+ /**
93
+ * Gateway timeout model 504
94
+ */
95
+ export interface GatewayTimeout extends TEVEErrorBase {
96
+ /**
97
+ * number of seconds the request was given
98
+ */
99
+ timeout?: number;
100
+ }
101
+ /**
102
+ * EVE server error model 520
103
+ */
104
+ export interface EVEServerError extends InternalServerError {}
@@ -43,24 +43,24 @@ export const request = async (method, endpoint, pathParams, opt) => {
43
43
  pathParams = [pathParams];
44
44
  }
45
45
  if (Array.isArray(pathParams)) {
46
- // @ts-expect-error actualy endp is string
47
46
  endpoint = util.replaceCbt(endpoint, pathParams);
48
47
  }
49
48
  // When only options are provided
50
49
  const actualOpt = /** @type {NonNullable<typeof opt>} */ (opt || pathParams || {});
51
50
  const { rqopt, qss } = util.initOptions(method, actualOpt);
52
- // @ts-expect-error actualy endp is string
53
51
  const endpointUrl = util.curl(endpoint);
54
52
  const up = new URLSearchParams(qss);
55
53
  const url = `${endpointUrl}${up.size ? `?${up}` : ""}`;
56
54
  DEBUG && log(url);
57
55
  try {
58
- return await fetch(url, rqopt).then(res => {
59
- if (res.status >= 400) {
60
- throw new util.ESIRequesError(`${res.statusText} (status=${res.status})`);
61
- }
62
- return res.json();
63
- });
56
+ const res = await fetch(url, rqopt);
57
+ // The parameters are different for successful and error responses.
58
+ if (util.isSuccess(res.status)) {
59
+ return util.handleSuccessResponse(res, endpointUrl, rqopt, up);
60
+ }
61
+ // else if (isError(status)) {}
62
+ // Actually, throw Error
63
+ throw await util.handleESIError(res, endpointUrl, actualOpt.cancelable);
64
64
  }
65
65
  catch (e) {
66
66
  log(e);
package/lib/rq-util.d.mts CHANGED
@@ -5,8 +5,22 @@
5
5
  // https://opensource.org/licenses/mit-license.php
6
6
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7
7
  */
8
- import "colors.ts";
8
+ /// <reference types="../v2"/>
9
+ // - - - - - - - - - - - - - - - - - - - -
10
+ // imports
11
+ // - - - - - - - - - - - - - - - - - - - -
9
12
  import type { TESIRequestFunctionMethods } from "../v2";
13
+ import type { TESIErrorStats } from "./esi-error-types";
14
+ /**
15
+ * Dummy import
16
+ * @import * as cc from "colors.ts";
17
+ */
18
+ /**
19
+ * will almost certainly be able to detect if you are in a nodejs environment
20
+ *
21
+ * @date 2020/5/9
22
+ */
23
+ export declare const isNode: boolean;
10
24
  /**
11
25
  * this always `https://esi.evetech.net`
12
26
  */
@@ -25,10 +39,6 @@ export type ESIRequestOptions = {
25
39
  * will need it for `POST` request etc.
26
40
  */
27
41
  body?: any;
28
- /**
29
- * if want response data with ignore error then can be set to `true`.
30
- */
31
- ignoreError?: boolean;
32
42
  /**
33
43
  * cancel request immediately
34
44
  */
@@ -46,6 +56,9 @@ export type ESIRequestOptions = {
46
56
  */
47
57
  auth?: true;
48
58
  };
59
+ /**
60
+ * @typedef {string | number | boolean} Truthy
61
+ */
49
62
  /**
50
63
  * simple named error class.
51
64
  */
@@ -61,12 +74,58 @@ export declare class ESIErrorLimitReachedError extends ESIRequesError {
61
74
  /**
62
75
  * @typedef {import("./rq-util.mjs").ESIRequestOptions} ESIRequestOptions
63
76
  */
77
+ /**
78
+ * #### status: 200 | 201 | 204
79
+ *
80
+ * + Returns a json object only if the status is `200` or `201`.
81
+ *
82
+ * @param {Response} response
83
+ * @param {string} endpointUrl
84
+ * @param {RequestInit} requestOpt
85
+ * @param {URLSearchParams} urlParams
86
+ * @param {(minus?: Truthy) => void=} increment
87
+ * @returns {Promise<any>}
88
+ */
89
+ export declare const handleSuccessResponse: (response: Response, endpointUrl: string, requestOpt: RequestInit, urlParams: URLSearchParams, increment?: (minus?: Truthy) => void) => Promise<any>;
90
+ /**
91
+ * @import {
92
+ * TESIErrorStats,
93
+ * TESIErrorStatMap,
94
+ * TESIErrorWithStat
95
+ * } from "./esi-error-types";
96
+ */
97
+ /**
98
+ * #### status: (400 | 401 | 403 | 404 | 420 | 422) or (500 | 503 | 504 | 520)
99
+ *
100
+ * @param {Response} res
101
+ * @param {string} endpointUrl
102
+ * @param {AbortController} abortable
103
+ * @returns {Promise<void>}
104
+ */
105
+ export declare const handleESIError: (res: Response, endpointUrl: string, abortable?: AbortController) => Promise<void>;
106
+ /**
107
+ * @param {number} stat
108
+ * @returns {stat is 200 | 201 | 204}
109
+ */
110
+ export declare const isSuccess: (stat: number) => stat is 200 | 201 | 204;
111
+ /**
112
+ * @param {number} stat
113
+ * @returns {stat is TESIErrorStats}
114
+ */
115
+ export declare const isError: (stat: number) => stat is TESIErrorStats;
116
+ /**
117
+ * @returns {boolean}
118
+ */
64
119
  export declare const isDebug: () => boolean;
120
+ /**
121
+ * @param {string} opt
122
+ * @returns {boolean}
123
+ */
65
124
  export declare const is: (opt: string) => boolean;
66
125
  /**
67
126
  * @param {string} method
68
127
  * @param {ESIRequestOptions} opt
69
- * @returns
128
+ * @returns {{ rqopt: RequestInit, qss: Record<string, string> }}
70
129
  */
71
130
  export declare const initOptions: (method: string, opt: ESIRequestOptions) => {
72
131
  rqopt: RequestInit;
@@ -76,12 +135,13 @@ export declare const initOptions: (method: string, opt: ESIRequestOptions) => {
76
135
  * fetch the extra pages
77
136
  *
78
137
  * + if the `x-pages` header property ware more than 1
79
- *
138
+ * @template {any} T
80
139
  * @param {string} endpointUrl
81
140
  * @param {RequestInit} rqopt request options
82
141
  * @param {URLSearchParams} rqp queries
83
142
  * @param {number} pc pageCount
84
143
  * @param {(minus?: number) => void=} increment
144
+ * @returns {Promise<T | null>}
85
145
  */
86
146
  export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: RequestInit, rqp: URLSearchParams, pc: number, increment?: (minus?: Truthy) => void) => Promise<T | null>;
87
147
  /** ### replace (C)urly (B)races (T)oken
@@ -91,17 +151,20 @@ export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: Req
91
151
  * // ->
92
152
  * "/characters/<char.character_id>/skills"
93
153
  *
94
- * @param {string} endpoint e.g - "/characters/{character_id}/"
154
+ * @template {unknown} T
155
+ * @param {T} endpoint e.g - "/characters/{character_id}/"
95
156
  * @param {number[]} ids
96
- * @returns fragment of qualified endpoint uri or null.
157
+ * @returns {T} fragment of qualified endpoint uri or null.
97
158
  */
98
- export declare const replaceCbt: (endpoint: string, ids: number[]) => string;
159
+ export declare const replaceCbt: <T extends unknown>(endpoint: T, ids: number[]) => T;
99
160
  /**
100
161
  *
101
- * @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
162
+ * @template {unknown} T
163
+ * @param {T} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
102
164
  * + The version parameter is forced to apply `latest`
165
+ * @returns {string}
103
166
  */
104
- export declare const curl: (endp: string) => string;
167
+ export declare const curl: <T extends unknown>(endp: T) => string;
105
168
  /**
106
169
  * @date 2020/03/31
107
170
  * @version 2.1
@@ -116,5 +179,6 @@ export declare function getLogger(): {
116
179
  * #### Fire a request that does not require authentication.
117
180
  *
118
181
  * @param {TESIRequestFunctionSignature<ESIRequestOptions> | TESIRequestFunctionMethods} fn
182
+ * @returns {Promise<void>}
119
183
  */
120
184
  export declare function fireRequestsDoesNotRequireAuth(fn: TESIRequestFunctionSignature<ESIRequestOptions> | TESIRequestFunctionMethods<ESIRequestOptions>): Promise<void>;
package/lib/rq-util.mjs CHANGED
@@ -9,7 +9,29 @@
9
9
  // - - - - - - - - - - - - - - - - - - - -
10
10
  // imports
11
11
  // - - - - - - - - - - - - - - - - - - - -
12
- import "colors.ts";
12
+ /**
13
+ * Dummy import
14
+ * @import * as cc from "colors.ts";
15
+ */
16
+ /**
17
+ * will almost certainly be able to detect if you are in a nodejs environment
18
+ *
19
+ * @date 2020/5/9
20
+ */
21
+ export const isNode = await (async () => {
22
+ let modId, returnValue;
23
+ if (typeof process === "object") {
24
+ modId = "colors.ts";
25
+ // @ts-ignore
26
+ returnValue = typeof process.versions === "object" && /\d+\.\d+\.\d+/.test(process.versions.node);
27
+ }
28
+ else {
29
+ modId = "https://cdn.jsdelivr.net/npm/colors.ts@1.0.20/+esm";
30
+ returnValue = false;
31
+ }
32
+ await import(modId);
33
+ return returnValue;
34
+ })();
13
35
  // - - - - - - - - - - - - - - - - - - - -
14
36
  // constants, types
15
37
  // - - - - - - - - - - - - - - - - - - - -
@@ -24,6 +46,9 @@ let LOG = false;
24
46
  * this always `https://esi.evetech.net`
25
47
  */
26
48
  export const BASE = "https://esi.evetech.net";
49
+ /**
50
+ * @typedef {string | number | boolean} Truthy
51
+ */
27
52
  /**
28
53
  * simple named error class.
29
54
  */
@@ -46,14 +71,144 @@ export class ESIErrorLimitReachedError extends ESIRequesError {
46
71
  // - - - - - - - - - - - - - - - - - - - -
47
72
  // utility functions
48
73
  // - - - - - - - - - - - - - - - - - - - -
74
+ /**
75
+ * #### status: 200 | 201 | 204
76
+ *
77
+ * + Returns a json object only if the status is `200` or `201`.
78
+ *
79
+ * @param {Response} response
80
+ * @param {string} endpointUrl
81
+ * @param {RequestInit} requestOpt
82
+ * @param {URLSearchParams} urlParams
83
+ * @param {(minus?: Truthy) => void=} increment
84
+ * @returns {Promise<any>}
85
+ */
86
+ export const handleSuccessResponse = async (response, endpointUrl, requestOpt, urlParams, increment = () => { }) => {
87
+ // NoContentResponse
88
+ if (response.status === 204)
89
+ return {};
90
+ /** @type {any} */
91
+ const data = await response.json();
92
+ // - - - - x-pages response.
93
+ // +undefined is NaN
94
+ // @ts-expect-error becouse +null is 0
95
+ const pc = +response.headers.get("x-pages");
96
+ // has remaining pages? NaN > 1 === false !isNaN(pageCount)
97
+ if (pc > 1) {
98
+ LOG && log('found "x-pages" header, pages: %d', pc);
99
+ const remData = await fetchP(endpointUrl, requestOpt, urlParams, pc, increment);
100
+ // finally, decide product data.
101
+ if (isArray(data) && isArray(remData)) {
102
+ // DEVNOTE: 2019/7/23 15:01:48 - types
103
+ return data.concat(remData);
104
+ }
105
+ else {
106
+ remData && Object.assign(data, remData);
107
+ }
108
+ }
109
+ return data;
110
+ };
111
+ /**
112
+ * @import {
113
+ * TESIErrorStats,
114
+ * TESIErrorStatMap,
115
+ * TESIErrorWithStat
116
+ * } from "./esi-error-types";
117
+ */
118
+ /**
119
+ * #### status: (400 | 401 | 403 | 404 | 420 | 422) or (500 | 503 | 504 | 520)
120
+ *
121
+ * @param {Response} res
122
+ * @param {string} endpointUrl
123
+ * @param {AbortController} abortable
124
+ * @returns {Promise<void>}
125
+ */
126
+ export const handleESIError = async (res, endpointUrl, abortable) => {
127
+ const status = /** @type {TESIErrorStats} */ (res.status);
128
+ /** @type {TESIErrorStatMap[typeof status]} */
129
+ const esiError = await res.json();
130
+ /** @type {TESIErrorWithStat<typeof status>} */
131
+ const errorType = {
132
+ status, ...esiError
133
+ };
134
+ // log ESI Error details
135
+ console.warn(errorType);
136
+ if (status === 420) {
137
+ abortable && abortable.abort();
138
+ throw new ESIErrorLimitReachedError();
139
+ }
140
+ else {
141
+ // console.log(res);
142
+ throw new ESIRequesError(`${res.statusText} (status=${status}, url=${endpointUrl})`);
143
+ }
144
+ };
145
+ /** @satisfies {TESIErrorStats[]} */
146
+ const ESIErrorStats = [
147
+ // status 400
148
+ 400, // BadRequest;
149
+ 401, // Unauthorized;
150
+ 403, // Forbidden;
151
+ 404, // NotFound;
152
+ 420, // ErrorLimited;
153
+ 422, // Unprocessable;
154
+ // status 500
155
+ 500, // InternalServerError;
156
+ 503, // ServiceUnavailable;
157
+ 504, // GatewayTimeout;
158
+ 520, // EVEServerError;
159
+ ];
160
+ /**
161
+ * @param {number} stat
162
+ * @returns {stat is 200 | 201 | 204}
163
+ */
164
+ export const isSuccess = (stat) => {
165
+ return stat === 200 || stat === 201 || stat === 204;
166
+ };
167
+ /**
168
+ * @param {number} stat
169
+ * @returns {stat is TESIErrorStats}
170
+ */
171
+ export const isError = (stat) => {
172
+ return ESIErrorStats.includes(/** @type {TESIErrorStats} */ (stat));
173
+ };
174
+ /**
175
+ * @returns {boolean}
176
+ */
49
177
  export const isDebug = () => {
50
- return process.argv.includes("-debug");
178
+ return is("debug");
179
+ };
180
+ /**
181
+ * @param {string} opt
182
+ * @returns {boolean}
183
+ */
184
+ export const is = (opt) => {
185
+ if (typeof process === "object") {
186
+ return process.argv.includes(`-${opt}`);
187
+ }
188
+ else {
189
+ const q = location.search || location.hash;
190
+ if (q) {
191
+ //* ctt
192
+ const entries = q.substring(1).split("&");
193
+ for (const entry of entries) {
194
+ const [key, /*value*/] = entry.split("=");
195
+ if (key === opt)
196
+ return true;
197
+ }
198
+ /*/
199
+ const usp = new URLSearchParams(q.substring(1));
200
+ for (const [key, value] of usp.entries()) {
201
+ if (key === opt) return true;
202
+ }
203
+ //*/
204
+ }
205
+ }
206
+ return false;
51
207
  };
52
- export const is = (opt) => process.argv.includes(`-${opt}`);
53
208
  /**
54
209
  * @param {string} method
55
210
  * @param {ESIRequestOptions} opt
56
- * @returns
211
+ * @returns {{ rqopt: RequestInit, qss: Record<string, string> }}
57
212
  */
58
213
  export const initOptions = (method, opt) => {
59
214
  /** @type {RequestInit} */
@@ -64,8 +219,11 @@ export const initOptions = (method, opt) => {
64
219
  signal: opt.cancelable?.signal,
65
220
  headers: {}
66
221
  };
222
+ /** @type {Record<string, string>} */
67
223
  const qss = {
68
- // language: "en",
224
+ // CAVEAT: If the language parameter is not set, some endpoints such as "/universe/ids/" may return incomplete results.
225
+ // Therefore, the language parameter should always be set.
226
+ language: "en",
69
227
  };
70
228
  if (opt.query) {
71
229
  // Object.assign(query, options.query); Object.assign is too slow
@@ -87,12 +245,13 @@ export const initOptions = (method, opt) => {
87
245
  * fetch the extra pages
88
246
  *
89
247
  * + if the `x-pages` header property ware more than 1
90
- *
248
+ * @template {any} T
91
249
  * @param {string} endpointUrl
92
250
  * @param {RequestInit} rqopt request options
93
251
  * @param {URLSearchParams} rqp queries
94
252
  * @param {number} pc pageCount
95
253
  * @param {(minus?: number) => void=} increment
254
+ * @returns {Promise<T | null>}
96
255
  */
97
256
  export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { }) => {
98
257
  const rqs = [];
@@ -113,7 +272,7 @@ export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { })
113
272
  for (let i = 0, end = jsons.length; i < end;) {
114
273
  combined = combined.concat(jsons[i++]);
115
274
  }
116
- return combined;
275
+ return /** @type {T} */ (combined);
117
276
  }
118
277
  LOG && log("> > > pages result are object < < < --", jsons);
119
278
  return null;
@@ -126,28 +285,26 @@ export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { })
126
285
  * // ->
127
286
  * "/characters/<char.character_id>/skills"
128
287
  *
129
- * @param {string} endpoint e.g - "/characters/{character_id}/"
288
+ * @template {unknown} T
289
+ * @param {T} endpoint e.g - "/characters/{character_id}/"
130
290
  * @param {number[]} ids
131
- * @returns fragment of qualified endpoint uri or null.
291
+ * @returns {T} fragment of qualified endpoint uri or null.
132
292
  */
133
293
  export const replaceCbt = (endpoint, ids) => {
134
- const re = /{([\w]+)}/g;
135
- /** @type {RegExpExecArray?} */
136
- let m;
137
294
  let idx = 0;
138
- while (m = re.exec(endpoint)) {
139
- endpoint = endpoint.replace(m[0], ids[idx++] + "");
140
- }
141
- return endpoint;
295
+ // @ts-expect-error
296
+ return endpoint.replace(/{([\w]+)}/g, $0 => ids[idx++] + "");
142
297
  };
143
298
  /**
144
299
  *
145
- * @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
300
+ * @template {unknown} T
301
+ * @param {T} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
146
302
  * + The version parameter is forced to apply `latest`
303
+ * @returns {string}
147
304
  */
148
305
  export const curl = (endp) => {
149
- endp = endp.replace(/^\/+|\/+$/g, "");
150
- return `${BASE}/latest/${endp}/`;
306
+ // @ts-expect-error
307
+ return `${BASE}/latest/${endp.replace(/^\/+|\/+$/g, "")}/`;
151
308
  };
152
309
  /**
153
310
  * @date 2020/03/31
@@ -160,7 +317,7 @@ export async function getSDEVersion() {
160
317
  const res = await fetch(sdeZipUrl, { method: "head", mode: "cors" });
161
318
  const date = res.headers.get("last-modified");
162
319
  if (date) {
163
- const YMD = new Date(date).toLocaleDateString('en-CA').replace(/-/g, '');
320
+ const YMD = new Date(date).toLocaleDateString("en-CA").replace(/-/g, "");
164
321
  return `sde-${YMD}-TRANQUILITY`;
165
322
  }
166
323
  else {
@@ -175,7 +332,7 @@ export async function getSDEVersion() {
175
332
  }
176
333
  export function getLogger() {
177
334
  const clog = console.log.bind(console, '- - -> Get the character data of "CCP Zoetrope"'.magenta);
178
- const rlog = console.log.bind(console, '- - -> Run ESI request'.cyan);
335
+ const rlog = console.log.bind(console, "- - -> Run ESI request".cyan);
179
336
  return { clog, rlog };
180
337
  }
181
338
  /**
@@ -212,6 +369,7 @@ function fireWithoutAuth(fn, method, endpoint, pathParams, opt) {
212
369
  * #### Fire a request that does not require authentication.
213
370
  *
214
371
  * @param {TESIRequestFunctionSignature<ESIRequestOptions> | TESIRequestFunctionMethods} fn
372
+ * @returns {Promise<void>}
215
373
  */
216
374
  export async function fireRequestsDoesNotRequireAuth(fn) {
217
375
  const { clog, rlog } = getLogger();
@@ -278,6 +436,7 @@ export async function fireRequestsDoesNotRequireAuth(fn) {
278
436
  },
279
437
  });
280
438
  // TODO: want TypeScript semantics to throw an error because there is a required query parameter, but it's not possible
439
+ // Or rather, I don't know how to do it.
281
440
  await fireWithoutAuth(fn, "get", "/characters/{character_id}/search/");
282
441
  log(ok);
283
442
  }
@@ -6,6 +6,7 @@
6
6
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
7
7
  */
8
8
  /// <reference types="../v2/esi-tagged-types"/>
9
+ import type { ESIRequestOptions } from "./rq-util.mjs";
9
10
  /**
10
11
  * `esi-tagged-types` and `injectESIRequestBody` allow for more intuitive use of ESI requests based on the "tags" defined in the EVE Swagger JSON.
11
12
  *
@@ -17,13 +18,18 @@
17
18
  * const esiRequest = taggedApi.injectESIRequestBody(...);
18
19
  * const ret = await esiRequest.universe.get("/universe/structures/", { query: { filter: "market" }});
19
20
  *
21
+ * @template {Record<string, unknown>} Opt
20
22
  * @param {TESIRequestFunctionSignature<{}>} requestBody
21
23
  * @returns {XESI.TaggedESIRequestMap}
22
24
  * @since 2.3
23
25
  */
24
- export declare function injectESIRequestBody(requestBody: TESIRequestFunctionSignature<{}>): XESI.TaggedESIRequestMap;
26
+ export declare function injectESIRequestBody<Opt extends Record<string, unknown>>(requestBody: TESIRequestFunctionSignature<Opt>): XESI.TaggedESIRequestMap<Opt>;
27
+ /**
28
+ * @import { ESIRequestOptions } from "./rq-util.mjs";
29
+ */
25
30
  /**
26
31
  * Injects the minimal implementation of ESI requests into `XESI.TaggedESIRequestMap`.
27
32
  * @since 2.3
33
+ * @type {XESI.TaggedESIRequestMap<ESIRequestOptions>}
28
34
  */
29
- export declare const esi: XESI.TaggedESIRequestMap;
35
+ export declare const esi: import("../v2/esi-tagged-types").TaggedESIRequestMap<ESIRequestOptions>;
@@ -10,27 +10,28 @@
10
10
  * @file eve-esi-types/lib/tagged-request-api.mts
11
11
  */
12
12
  import { request } from "./request-api.mjs";
13
- /** @satisfies {XESI.ESITags[]} */
14
- const ESI_TAGs = [
15
- "Alliance", "Assets",
16
- "Calendar", "Character",
17
- "Clones", "Contacts",
18
- "Contracts", "Corporation",
19
- "Dogma", "Faction Warfare",
20
- "Fittings", "Fleets",
21
- "Incursions", "Industry",
22
- "Insurance", "Killmails",
23
- "Location", "Loyalty",
24
- "Mail", "Market",
25
- "Opportunities", "Planetary Interaction",
26
- "Routes", "Search",
27
- "Skills", "Sovereignty",
28
- "Status", "Universe",
29
- "User Interface", "Wallet",
30
- "Wars"
13
+ /**
14
+ * @typedef {`${string}${"" | `,${string}`}`} TMethodList
15
+ */
16
+ /** @satisfies {`${XESI.ESITags}:${TMethodList}`[]} */
17
+ const ESITagsWithMethodList = [
18
+ "Alliance:get", "Assets:get,post",
19
+ "Calendar:get,put", "Character:get,post",
20
+ "Clones:get", "Contacts:get,post,put,delete",
21
+ "Contracts:get", "Corporation:get",
22
+ "Dogma:get", "Faction Warfare:get",
23
+ "Fittings:get,post,delete", "Fleets:get,post,put,delete",
24
+ "Incursions:get", "Industry:get",
25
+ "Insurance:get", "Killmails:get",
26
+ "Location:get", "Loyalty:get",
27
+ "Mail:get,post,put,delete", "Market:get",
28
+ "Opportunities:get", "Planetary Interaction:get",
29
+ "Routes:get", "Search:get",
30
+ "Skills:get", "Sovereignty:get",
31
+ "Status:get", "Universe:get,post",
32
+ "User Interface:post", "Wallet:get",
33
+ "Wars:get"
31
34
  ];
32
- /** @satisfies {TESIEntryMethod[]} */
33
- const METHODs = ["get", "post", "put", "delete"];
34
35
  /**
35
36
  * `esi-tagged-types` and `injectESIRequestBody` allow for more intuitive use of ESI requests based on the "tags" defined in the EVE Swagger JSON.
36
37
  *
@@ -42,28 +43,32 @@ const METHODs = ["get", "post", "put", "delete"];
42
43
  * const esiRequest = taggedApi.injectESIRequestBody(...);
43
44
  * const ret = await esiRequest.universe.get("/universe/structures/", { query: { filter: "market" }});
44
45
  *
46
+ * @template {Record<string, unknown>} Opt
45
47
  * @param {TESIRequestFunctionSignature<{}>} requestBody
46
48
  * @returns {XESI.TaggedESIRequestMap}
47
49
  * @since 2.3
48
50
  */
49
- // Here, functions are implemented for "get", "post", "put", and "delete" for convenience.
50
- // However, in the completion listed by `injectESIRequestBody(<requestBody>).`, only the inferred methods will be listed.
51
51
  export function injectESIRequestBody(requestBody) {
52
- const rq = /** @type {XESI.TaggedESIRequestMap} */ ({});
53
- for (const tag of ESI_TAGs) {
54
- const camel =
55
- /** @type {XESI.LCamelCase<XESI.ESITags>} */ (tag[0].toLowerCase() + tag.slice(1).replace(/\s(\w)/g, "$1"));
56
- const entry = /** @type {XESI.ESITaggedEndpointRequest<typeof tag>} */ ({});
57
- for (const method of METHODs) {
58
- // @ts-ignore ignore ts(2590)
52
+ const rq = /** @type {XESI.TaggedESIRequestMap<Opt>} */ ({});
53
+ for (const tagEntry of ESITagsWithMethodList) {
54
+ const [tag, methodList] = /** @type {[tag: XESI.ESITags, methods: TMethodList]} */ (tagEntry.split(":"));
55
+ const methods = /** @type {TESIEntryMethod[]} */ (methodList.split(","));
56
+ const entry = /** @type {XESI.ESITaggedEndpointRequest<typeof tag, Opt>} */ ({});
57
+ for (const method of methods) {
58
+ // @ts-expect-error
59
59
  entry[method] = /** @satisfies {XESI.TaggedEndpointRequestFunction<typeof method, typeof tag>} */ ((e, params, opt) => requestBody(method, e, params, opt));
60
60
  }
61
- rq[camel] = entry;
61
+ const camelCased = /** @type {XESI.LCamelCase<XESI.ESITags>} */ (tag[0].toLowerCase() + tag.slice(1).replace(/\s(.)/g, "$1"));
62
+ rq[camelCased] = entry;
62
63
  }
63
64
  return rq;
64
65
  }
66
+ /**
67
+ * @import { ESIRequestOptions } from "./rq-util.mjs";
68
+ */
65
69
  /**
66
70
  * Injects the minimal implementation of ESI requests into `XESI.TaggedESIRequestMap`.
67
71
  * @since 2.3
72
+ * @type {XESI.TaggedESIRequestMap<ESIRequestOptions>}
68
73
  */
69
74
  export const esi = injectESIRequestBody(request);
package/minimal-rq.mjs CHANGED
@@ -27,11 +27,10 @@ const log = console.log;
27
27
  // Delegates implementation to `request` (TESIRequestFunctionMethods)
28
28
  //
29
29
  const esiMethods = /** @type {TESIRequestFunctionMethods} */ ({});
30
- ["get", "post", "put", "delete"].forEach((method) => {
31
- esiMethods[method] = function (endpoint, params, opt) {
32
- // @ts-ignore
30
+ /** @type {TESIEntryMethod[]} */ (["get", "post", "put", "delete"]).forEach((method) => {
31
+ esiMethods[method] = /** @type {TESIRequestFunctionEachMethod<typeof method>} */ (function (endpoint, params, opt) {
33
32
  return request(method, endpoint, params, opt);
34
- };
33
+ });
35
34
  });
36
35
  // It should complete correctly.
37
36
  /**
@@ -84,6 +83,12 @@ async function getEVEStatus(fn) {
84
83
  if (!util.is("x")) {
85
84
  getEVEStatus(request).then(eveStatus => log(eveStatus));
86
85
  }
86
+ else {
87
+ // @ts-ignore
88
+ globalThis.runTest = () => {
89
+ getEVEStatus(request).then(eveStatus => log(eveStatus));
90
+ };
91
+ }
87
92
  // {
88
93
  // "players": 16503,
89
94
  // "server_version": "2794925",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve-esi-types",
3
- "version": "2.3.0",
3
+ "version": "2.3.3",
4
4
  "description": "Extracted the main type of ESI. use for ESI request response types (version 2 only)",
5
5
  "main": "v2/index.d.ts",
6
6
  "scripts": {
@@ -15,6 +15,7 @@
15
15
  "v2",
16
16
  "lib",
17
17
  "*.d.mts",
18
+ "index.html",
18
19
  "*.mjs",
19
20
  "LICENSE",
20
21
  "*.md",
package/tagged-rq.mjs CHANGED
@@ -17,6 +17,7 @@ esiRq.universe.get("/universe/structures/", { query: { filter: "market" } }).the
17
17
  esiRq.universe.post("/universe/ids/", {
18
18
  body: ["the forge", "plex"]
19
19
  }).then(console.log);
20
+ esiRq.fittings.delete("/characters/{character_id}/fittings/{fitting_id}/", [1234, 56789]);
20
21
  esiRq.assets.get("/characters/{character_id}/assets/", 1234, {
21
22
  auth: true
22
23
  }).then(console.log).catch(console.log);
package/tsconfig.json CHANGED
@@ -18,6 +18,7 @@
18
18
  },
19
19
  "include": [
20
20
  "./scripts/**/*.mts",
21
+ "./scripts/**/*.d.ts",
21
22
  "./v2/*.d.ts"
22
23
  ],
23
24
  "exclude": [
@@ -9,7 +9,7 @@
9
9
  * THIS DTS IS AUTO GENERATED, DO NOT EDIT
10
10
  *
11
11
  * @file eve-esi-types/v2/esi-tagged-types.d.ts
12
- * @summary This file is auto-generated and defines version 2.3.0 of the EVE Online ESI response types.
12
+ * @summary This file is auto-generated and defines version 2.3.2 of the EVE Online ESI response types.
13
13
  */
14
14
  import { TESIResponseOKMap } from "./index.d.ts";
15
15
  export * from "./index.d.ts";
@@ -29,6 +29,19 @@ export * from "./index.d.ts";
29
29
  export declare type LCamelCase<S extends string> = S extends `${infer P1} ${infer P2}`
30
30
  ? `${Lowercase<P1>}${Capitalize<P2>}` : Lowercase<S>;
31
31
 
32
+
33
+ declare const enum EInferSomethingBy {
34
+ METHOD,
35
+ TAGs
36
+ }
37
+ declare type InferSomethingBy<Tag, AsType extends EInferSomethingBy = EInferSomethingBy.METHOD> = {
38
+ [M in TESIEntryMethod]: TESIResponseOKMap[M] extends Record<`/${string}/`, { tag: infer ActualTag }>
39
+ ? AsType extends EInferSomethingBy.TAGs
40
+ ? ActualTag : ActualTag extends Tag
41
+ ? M
42
+ : never
43
+ : never;
44
+ }[TESIEntryMethod];
32
45
  // - - - - - - - - - - - - - - - - - - - - - - - - - -
33
46
  // Utility Type `ESITags`
34
47
  // - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -37,10 +50,7 @@ export declare type LCamelCase<S extends string> = S extends `${infer P1} ${infe
37
50
  * @template M - The HTTP method.
38
51
  * @date 2025/2/28
39
52
  */
40
- export declare type ESITags = {
41
- [M in TESIEntryMethod]: TESIResponseOKMap[M] extends Record<`/${string}/`, { tag: infer Tag }>
42
- ? Tag : never
43
- }[TESIEntryMethod];
53
+ export declare type ESITags = InferSomethingBy<never, EInferSomethingBy.TAGs>
44
54
 
45
55
  // - - - - - - - - - - - - - - - - - - - - - - - - - -
46
56
  // Utility Type `InferMethod`
@@ -51,12 +61,7 @@ export declare type ESITags = {
51
61
  * @template Tag - The tag to infer the method for.
52
62
  * @date 2025/2/28
53
63
  */
54
- export declare type InferMethod<Tag> = {
55
- [M in TESIEntryMethod]: TESIResponseOKMap[M] extends Record<`/${string}/`, { tag: infer ActualTag }>
56
- ? ActualTag extends Tag
57
- ? M : never
58
- : never;
59
- }[TESIEntryMethod];
64
+ export declare type InferMethod<Tag> = InferSomethingBy<Tag>
60
65
  // type XAssets = InferMethod<"Assets">;
61
66
  // type XContacts = InferMethod<"Contacts">;
62
67
  // type XPlanetary = InferMethod<"Planetary Interaction">;
@@ -97,9 +102,9 @@ export declare type TaggedEndpointRequestFunction<M extends TESIEntryMethod, Tag
97
102
  * @template Tag - The tag to map.
98
103
  * @date 2025/2/28
99
104
  */
100
- export declare type ESITaggedEndpointRequest<Tag extends ESITags> = {
105
+ export declare type ESITaggedEndpointRequest<Tag extends ESITags, ActualOpt = {}> = {
101
106
  [tag in Tag]: {
102
- [method in InferMethod<Tag>]: TaggedEndpointRequestFunction<method, tag>;
107
+ [method in InferMethod<Tag>]: TaggedEndpointRequestFunction<method, tag, ActualOpt>;
103
108
  };
104
109
  }[Tag];
105
110
 
@@ -129,8 +134,8 @@ export declare type SelectEndpointByTag<
129
134
  * Maps lower camel case tags to their corresponding endpoint request functions.
130
135
  * @date 2025/2/28
131
136
  */
132
- export declare type TaggedESIRequestMap = {
133
- [tag in ESITags as LCamelCase<tag>]: ESITaggedEndpointRequest<tag>;
137
+ export declare type TaggedESIRequestMap<ActualOpt = {}> = {
138
+ [tag in ESITags as LCamelCase<tag>]: ESITaggedEndpointRequest<tag, ActualOpt>;
134
139
  };
135
140
 
136
141
  /**
package/v2/index.d.ts CHANGED
@@ -9,12 +9,14 @@
9
9
  * THIS DTS IS AUTO GENERATED, DO NOT EDIT
10
10
  *
11
11
  * @file eve-esi-types/v2/index.d.ts
12
- * @summary This file is auto-generated and defines version 2.3.0 of the EVE Online ESI response types.
12
+ * @summary This file is auto-generated and defines version 2.3.2 of the EVE Online ESI response types.
13
13
  */
14
14
 
15
15
  import type { TESIResponseOKMap } from "./response-map.d.ts";
16
16
  export type { TESIResponseOKMap } from "./response-map.d.ts";
17
17
 
18
+ import type { PickPathParameters, InferKeysLen } from "./util.d.ts";
19
+
18
20
  /**
19
21
  * Represents a function that can make ESI requests with various HTTP methods.
20
22
  *
@@ -73,6 +75,20 @@ declare global {
73
75
  */
74
76
  type RequireThese<T, K extends keyof T> = T & Required<Pick<T, K>>;
75
77
 
78
+ /**
79
+ * If `EP` (endpoint) is a parameterized path, determines the required number of replacements.
80
+ *
81
+ * @template EP The string representing the endpoint path.
82
+ * @template Opt The type to return if `EP` is not parameterized.
83
+ * @returns {number | [number, number] | Opt}
84
+ * Returns `number` if there is one parameter, `[number, number]` if there are two parameters, otherwise `Opt`.
85
+ */
86
+ type IfParameterizedPath<EP, Opt> = EP extends `${string}/{${string}}${string}`
87
+ ? PickPathParameters<EP> extends never
88
+ ? Opt : InferKeysLen<PickPathParameters<EP>> extends 1
89
+ ? number : [number, number]
90
+ : Opt;
91
+
76
92
  /**
77
93
  * ### ESI request function all in one signature
78
94
  *
@@ -117,15 +133,6 @@ declare global {
117
133
  R extends InferESIResponseResult<M, EP>
118
134
  >(endpoint: EP, pathParams?: P2, options?: Opt) => Promise<R>;
119
135
 
120
- // /**
121
- // * is parameterized path
122
- // */
123
- // type IsParameterizedPath<EP, A, B> = EP extends `${string}/{${string}}/${string | ""}` ? A: B;
124
- /**
125
- * if parameterized path then specify number type, otherwise will be `Opt` type.
126
- */
127
- type IfParameterizedPath<EP, Opt> = EP extends `${string}/{${string}}/${string | ""}` ? number | number[]: Opt;
128
-
129
136
  /**
130
137
  * Identifies the required parameters for a given entry type.
131
138
  *
@@ -158,9 +165,8 @@ declare global {
158
165
 
159
166
  /**
160
167
  * Represents a response with no content (HTTP status 204).
161
- * Although no data is returned, it indicates successful completion by returning a status of 204.
162
168
  */
163
- type NoContentResponse = { status: 204 };
169
+ type NoContentResponse = { /* status: 204 */ };
164
170
 
165
171
  /**
166
172
  * Represents the HTTP methods supported by ESI.
@@ -9,7 +9,7 @@
9
9
  * THIS DTS IS AUTO GENERATED, DO NOT EDIT
10
10
  *
11
11
  * @file eve-esi-types/v2/response-map.d.ts
12
- * @summary This file is auto-generated and defines version 2.3.0 of the EVE Online ESI response types.
12
+ * @summary This file is auto-generated and defines version 2.3.2 of the EVE Online ESI response types.
13
13
  */
14
14
  import "./types-index.d.ts";
15
15
 
@@ -9,7 +9,7 @@
9
9
  * THIS DTS IS AUTO GENERATED, DO NOT EDIT
10
10
  *
11
11
  * @file eve-esi-types/v2/types-index.d.ts
12
- * @summary This file is auto-generated and defines version 2.3.0 of the EVE Online ESI response types.
12
+ * @summary This file is auto-generated and defines version 2.3.2 of the EVE Online ESI response types.
13
13
  */
14
14
  import "./get_wars_ok.d.ts";
15
15
  import "./get_status_ok.d.ts";
package/v2/util.d.ts ADDED
@@ -0,0 +1,51 @@
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
+ * @file eve-esi-types/v2/util.d.ts
10
+ * @since 2.3.1
11
+ */
12
+
13
+ /**
14
+ * If `Path` is parameterized, return the parameter name, such as `killmail_id`.
15
+ *
16
+ * @template Path The string representing the endpoint path.
17
+ * @returns {string | never} The parameter name if the path is parameterized, otherwise `never`.
18
+ */
19
+ export type PickPathParameters<Path extends string> =
20
+ Path extends `${string}/{${infer Param}}/${infer Rest}`
21
+ ? Param | PickPathParameters<`/${Rest}`>
22
+ : never;
23
+
24
+ /**
25
+ * Convert a union type to an intersection type.
26
+ *
27
+ * @template U The union type to convert.
28
+ * @returns {I} The intersection type.
29
+ */
30
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
31
+
32
+ /**
33
+ * Convert a union type to a tuple.
34
+ *
35
+ * @template T The union type to convert.
36
+ * @returns {Array} The tuple representation of the union type.
37
+ */
38
+ type UnionToTuple<T> = UnionToIntersection<
39
+ T extends any ? () => T : never
40
+ > extends () => infer R ? [...UnionToTuple<Exclude<T, R>>, R] : [];
41
+
42
+ /**
43
+ * #### Build an array of elements from a flattened tuple of type "1" | "2" | "3" ...
44
+ *
45
+ * + Returns the final length of the array.
46
+ *
47
+ * @template T The union type to be converted to a tuple and measured.
48
+ * @returns {number} The length of the tuple.
49
+ * @date 2025/2/11 18:12:02
50
+ */
51
+ export type InferKeysLen<T> = UnionToTuple<T>["length"];
package/v2.d.mts CHANGED
@@ -7,6 +7,10 @@
7
7
  */
8
8
  import type { TESIResponseOKMap } from "./v2";
9
9
  import { type ESIRequestOptions } from "./lib/rq-util.mjs";
10
+ /**
11
+ * @returns Get The Current ESI request pending count.
12
+ */
13
+ export declare const getRequestPending: () => number;
10
14
  /**
11
15
  * fire ESI request
12
16
  * @template {TESIEntryMethod} M
package/v2.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // import type { TESIResponseOKMap } from "eve-esi-types";
2
- import { is, curl, fetchP, replaceCbt, getSDEVersion, ESIRequesError, initOptions, isDebug, ESIErrorLimitReachedError, fireRequestsDoesNotRequireAuth } from "./lib/rq-util.mjs";
2
+ import { is, curl, replaceCbt, getSDEVersion, initOptions, isDebug, fireRequestsDoesNotRequireAuth, isSuccess, handleESIError, handleSuccessResponse } from "./lib/rq-util.mjs";
3
3
  // - - - - - - - - - - - - - - - - - - - -
4
4
  // constants, types
5
5
  // - - - - - - - - - - - - - - - - - - - -
@@ -22,6 +22,10 @@ let LOG = isDebug();
22
22
  */
23
23
  let ax = 0;
24
24
  const incrementAx = (minus) => minus ? ax-- : ax++;
25
+ /**
26
+ * @returns Get The Current ESI request pending count.
27
+ */
28
+ export const getRequestPending = () => ax;
25
29
  // - - - - - - - - - - - - - - - - - - - -
26
30
  // main functions
27
31
  // - - - - - - - - - - - - - - - - - - - -
@@ -47,7 +51,6 @@ export async function fire(mthd, endp, pathParams, opt) {
47
51
  pathParams = [pathParams]; // as unknown as P2;
48
52
  }
49
53
  if (isArray(pathParams)) {
50
- // @ts-expect-error actualy endp is string
51
54
  endp = replaceCbt(endp, pathParams);
52
55
  }
53
56
  // When only options are provided
@@ -55,7 +58,6 @@ export async function fire(mthd, endp, pathParams, opt) {
55
58
  // @ts-ignore
56
59
  const actualOpt = opt || pathParams || {};
57
60
  const { rqopt, qss } = initOptions(mthd, actualOpt);
58
- // @ts-expect-error actualy endp is string
59
61
  const endpointUrl = curl(endp);
60
62
  const up = new URLSearchParams(qss);
61
63
  const url = `${endpointUrl}${up.size ? `?${up}` : ""}`;
@@ -63,53 +65,16 @@ export async function fire(mthd, endp, pathParams, opt) {
63
65
  ax++;
64
66
  try {
65
67
  const res = await fetch(url, rqopt).finally(() => ax--);
66
- const { status } = res;
67
- if (!res.ok && !actualOpt.ignoreError) {
68
- if (status === 420) {
69
- actualOpt.cancelable && actualOpt.cancelable.abort();
70
- throw new ESIErrorLimitReachedError();
71
- }
72
- else {
73
- // console.log(res);
74
- throw new ESIRequesError(`${res.statusText} (status=${status})`);
75
- }
76
- }
77
- else {
78
- // DEVNOTE: - 204 No Content
79
- if (status === 204) {
80
- // this result is empty, decided to return status code.
81
- return /** @type {R} */ ({ status });
82
- }
83
- /** @type {R} */
84
- const data = await res.json();
85
- if (actualOpt.ignoreError) {
86
- // meaning `forceJson`?
87
- return data;
88
- }
89
- // - - - - x-pages response.
90
- // +undefined is NaN
91
- // @ts-expect-error becouse +null is 0
92
- const pc = +res.headers.get("x-pages");
93
- // has remaining pages? NaN > 1 === false !isNaN(pageCount)
94
- if (pc > 1) {
95
- LOG && log('found "x-pages" header, pages: %d', pc);
96
- const remData = await fetchP(endpointUrl, rqopt, up, pc, incrementAx);
97
- // finally, decide product data.
98
- if (isArray(data) && isArray(remData)) {
99
- // DEVNOTE: 2019/7/23 15:01:48 - types
100
- return /** @type {R} */ (data.concat(remData));
101
- }
102
- else {
103
- // @ts-expect-error TODO: fix type
104
- remData && Object.assign(data, remData);
105
- }
106
- }
107
- return data;
68
+ // The parameters are different for successful and error responses.
69
+ if (isSuccess(res.status)) {
70
+ return handleSuccessResponse(res, endpointUrl, rqopt, up, incrementAx);
108
71
  }
72
+ // else if (isError(status)) {}
73
+ // Actually, throw Error
74
+ throw await handleESIError(res, endpointUrl, actualOpt.cancelable);
109
75
  }
110
76
  catch (e) {
111
- // @ts-expect-error actualy endp is string
112
- throw new ESIRequesError(`message: ${e.message}, endpoint=${endp}`);
77
+ throw e;
113
78
  }
114
79
  }
115
80
  // It should complete correctly.