eve-esi-types 2.3.0 → 2.3.2
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 +5 -1
- package/lib/esi-error-types.d.ts +104 -0
- package/lib/request-api.mjs +9 -6
- package/lib/rq-util.d.mts +56 -7
- package/lib/rq-util.mjs +122 -13
- package/lib/tagged-request-api.d.mts +8 -2
- package/lib/tagged-request-api.mjs +35 -30
- package/minimal-rq.mjs +3 -4
- package/package.json +1 -1
- package/tagged-rq.mjs +1 -0
- package/tsconfig.json +1 -0
- package/v2/esi-tagged-types.d.ts +20 -15
- package/v2/index.d.ts +18 -12
- package/v2/response-map.d.ts +1 -1
- package/v2/types-index.d.ts +1 -1
- package/v2/util.d.ts +51 -0
- package/v2.d.mts +4 -0
- package/v2.mjs +12 -44
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}}
|
|
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
|
|
@@ -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 {}
|
package/lib/request-api.mjs
CHANGED
|
@@ -55,12 +55,15 @@ export const request = async (method, endpoint, pathParams, opt) => {
|
|
|
55
55
|
const url = `${endpointUrl}${up.size ? `?${up}` : ""}`;
|
|
56
56
|
DEBUG && log(url);
|
|
57
57
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return
|
|
63
|
-
}
|
|
58
|
+
const res = await fetch(url, rqopt);
|
|
59
|
+
const { status } = res;
|
|
60
|
+
// The parameters are different for successful and error responses.
|
|
61
|
+
if (util.isSuccess(status)) {
|
|
62
|
+
return util.handleSuccessResponse(res, endpointUrl, rqopt, up);
|
|
63
|
+
}
|
|
64
|
+
// else if (isError(status)) {}
|
|
65
|
+
// Actually, throw Error
|
|
66
|
+
throw await util.handleESIError(res, endpointUrl, actualOpt.cancelable);
|
|
64
67
|
}
|
|
65
68
|
catch (e) {
|
|
66
69
|
log(e);
|
package/lib/rq-util.d.mts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import "colors.ts";
|
|
9
9
|
import type { TESIRequestFunctionMethods } from "../v2";
|
|
10
|
+
import type { TESIErrorStats } from "./esi-error-types";
|
|
10
11
|
/**
|
|
11
12
|
* this always `https://esi.evetech.net`
|
|
12
13
|
*/
|
|
@@ -25,10 +26,6 @@ export type ESIRequestOptions = {
|
|
|
25
26
|
* will need it for `POST` request etc.
|
|
26
27
|
*/
|
|
27
28
|
body?: any;
|
|
28
|
-
/**
|
|
29
|
-
* if want response data with ignore error then can be set to `true`.
|
|
30
|
-
*/
|
|
31
|
-
ignoreError?: boolean;
|
|
32
29
|
/**
|
|
33
30
|
* cancel request immediately
|
|
34
31
|
*/
|
|
@@ -46,6 +43,9 @@ export type ESIRequestOptions = {
|
|
|
46
43
|
*/
|
|
47
44
|
auth?: true;
|
|
48
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {string | number | boolean} Truthy
|
|
48
|
+
*/
|
|
49
49
|
/**
|
|
50
50
|
* simple named error class.
|
|
51
51
|
*/
|
|
@@ -61,12 +61,58 @@ export declare class ESIErrorLimitReachedError extends ESIRequesError {
|
|
|
61
61
|
/**
|
|
62
62
|
* @typedef {import("./rq-util.mjs").ESIRequestOptions} ESIRequestOptions
|
|
63
63
|
*/
|
|
64
|
+
/**
|
|
65
|
+
* #### status: 200 | 201 | 204
|
|
66
|
+
*
|
|
67
|
+
* + Returns a json object only if the status is `200` or `201`.
|
|
68
|
+
*
|
|
69
|
+
* @param {Response} response
|
|
70
|
+
* @param {string} endpointUrl
|
|
71
|
+
* @param {RequestInit} requestOpt
|
|
72
|
+
* @param {URLSearchParams} urlParams
|
|
73
|
+
* @param {(minus?: Truthy) => void=} increment
|
|
74
|
+
* @returns {Promise<any>}
|
|
75
|
+
*/
|
|
76
|
+
export declare const handleSuccessResponse: (response: Response, endpointUrl: string, requestOpt: RequestInit, urlParams: URLSearchParams, increment?: (minus?: Truthy) => void) => Promise<any>;
|
|
77
|
+
/**
|
|
78
|
+
* @import {
|
|
79
|
+
* TESIErrorStats,
|
|
80
|
+
* TESIErrorStatMap,
|
|
81
|
+
* TESIErrorWithStat
|
|
82
|
+
* } from "./esi-error-types";
|
|
83
|
+
*/
|
|
84
|
+
/**
|
|
85
|
+
* #### status: (400 | 401 | 403 | 404 | 420 | 422) or (500 | 503 | 504 | 520)
|
|
86
|
+
*
|
|
87
|
+
* @param {Response} res
|
|
88
|
+
* @param {string} endpointUrl
|
|
89
|
+
* @param {AbortController} abortable
|
|
90
|
+
* @returns {Promise<void>}
|
|
91
|
+
*/
|
|
92
|
+
export declare const handleESIError: (res: Response, endpointUrl: string, abortable?: AbortController) => Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* @param {number} stat
|
|
95
|
+
* @returns {stat is 200 | 201 | 204}
|
|
96
|
+
*/
|
|
97
|
+
export declare const isSuccess: (stat: number) => stat is 200 | 201 | 204;
|
|
98
|
+
/**
|
|
99
|
+
* @param {number} stat
|
|
100
|
+
* @returns {stat is TESIErrorStats}
|
|
101
|
+
*/
|
|
102
|
+
export declare const isError: (stat: number) => stat is TESIErrorStats;
|
|
103
|
+
/**
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
64
106
|
export declare const isDebug: () => boolean;
|
|
107
|
+
/**
|
|
108
|
+
* @param {string} opt
|
|
109
|
+
* @returns {boolean}
|
|
110
|
+
*/
|
|
65
111
|
export declare const is: (opt: string) => boolean;
|
|
66
112
|
/**
|
|
67
113
|
* @param {string} method
|
|
68
114
|
* @param {ESIRequestOptions} opt
|
|
69
|
-
* @returns
|
|
115
|
+
* @returns {{ rqopt: RequestInit, qss: Record<string, string> }}
|
|
70
116
|
*/
|
|
71
117
|
export declare const initOptions: (method: string, opt: ESIRequestOptions) => {
|
|
72
118
|
rqopt: RequestInit;
|
|
@@ -76,12 +122,13 @@ export declare const initOptions: (method: string, opt: ESIRequestOptions) => {
|
|
|
76
122
|
* fetch the extra pages
|
|
77
123
|
*
|
|
78
124
|
* + if the `x-pages` header property ware more than 1
|
|
79
|
-
*
|
|
125
|
+
* @template {any} T
|
|
80
126
|
* @param {string} endpointUrl
|
|
81
127
|
* @param {RequestInit} rqopt request options
|
|
82
128
|
* @param {URLSearchParams} rqp queries
|
|
83
129
|
* @param {number} pc pageCount
|
|
84
130
|
* @param {(minus?: number) => void=} increment
|
|
131
|
+
* @returns {Promise<T | null>}
|
|
85
132
|
*/
|
|
86
133
|
export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: RequestInit, rqp: URLSearchParams, pc: number, increment?: (minus?: Truthy) => void) => Promise<T | null>;
|
|
87
134
|
/** ### replace (C)urly (B)races (T)oken
|
|
@@ -93,13 +140,14 @@ export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: Req
|
|
|
93
140
|
*
|
|
94
141
|
* @param {string} endpoint e.g - "/characters/{character_id}/"
|
|
95
142
|
* @param {number[]} ids
|
|
96
|
-
* @returns fragment of qualified endpoint uri or null.
|
|
143
|
+
* @returns {string | null} fragment of qualified endpoint uri or null.
|
|
97
144
|
*/
|
|
98
145
|
export declare const replaceCbt: (endpoint: string, ids: number[]) => string;
|
|
99
146
|
/**
|
|
100
147
|
*
|
|
101
148
|
* @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
|
|
102
149
|
* + The version parameter is forced to apply `latest`
|
|
150
|
+
* @returns {string}
|
|
103
151
|
*/
|
|
104
152
|
export declare const curl: (endp: string) => string;
|
|
105
153
|
/**
|
|
@@ -116,5 +164,6 @@ export declare function getLogger(): {
|
|
|
116
164
|
* #### Fire a request that does not require authentication.
|
|
117
165
|
*
|
|
118
166
|
* @param {TESIRequestFunctionSignature<ESIRequestOptions> | TESIRequestFunctionMethods} fn
|
|
167
|
+
* @returns {Promise<void>}
|
|
119
168
|
*/
|
|
120
169
|
export declare function fireRequestsDoesNotRequireAuth(fn: TESIRequestFunctionSignature<ESIRequestOptions> | TESIRequestFunctionMethods<ESIRequestOptions>): Promise<void>;
|
package/lib/rq-util.mjs
CHANGED
|
@@ -24,6 +24,9 @@ let LOG = false;
|
|
|
24
24
|
* this always `https://esi.evetech.net`
|
|
25
25
|
*/
|
|
26
26
|
export const BASE = "https://esi.evetech.net";
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {string | number | boolean} Truthy
|
|
29
|
+
*/
|
|
27
30
|
/**
|
|
28
31
|
* simple named error class.
|
|
29
32
|
*/
|
|
@@ -46,14 +49,121 @@ export class ESIErrorLimitReachedError extends ESIRequesError {
|
|
|
46
49
|
// - - - - - - - - - - - - - - - - - - - -
|
|
47
50
|
// utility functions
|
|
48
51
|
// - - - - - - - - - - - - - - - - - - - -
|
|
52
|
+
/**
|
|
53
|
+
* #### status: 200 | 201 | 204
|
|
54
|
+
*
|
|
55
|
+
* + Returns a json object only if the status is `200` or `201`.
|
|
56
|
+
*
|
|
57
|
+
* @param {Response} response
|
|
58
|
+
* @param {string} endpointUrl
|
|
59
|
+
* @param {RequestInit} requestOpt
|
|
60
|
+
* @param {URLSearchParams} urlParams
|
|
61
|
+
* @param {(minus?: Truthy) => void=} increment
|
|
62
|
+
* @returns {Promise<any>}
|
|
63
|
+
*/
|
|
64
|
+
export const handleSuccessResponse = async (response, endpointUrl, requestOpt, urlParams, increment = () => { }) => {
|
|
65
|
+
// NoContentResponse
|
|
66
|
+
if (response.status === 204)
|
|
67
|
+
return {};
|
|
68
|
+
/** @type {any} */
|
|
69
|
+
const data = await response.json();
|
|
70
|
+
// - - - - x-pages response.
|
|
71
|
+
// +undefined is NaN
|
|
72
|
+
// @ts-expect-error becouse +null is 0
|
|
73
|
+
const pc = +response.headers.get("x-pages");
|
|
74
|
+
// has remaining pages? NaN > 1 === false !isNaN(pageCount)
|
|
75
|
+
if (pc > 1) {
|
|
76
|
+
LOG && log('found "x-pages" header, pages: %d', pc);
|
|
77
|
+
const remData = await fetchP(endpointUrl, requestOpt, urlParams, pc, increment);
|
|
78
|
+
// finally, decide product data.
|
|
79
|
+
if (isArray(data) && isArray(remData)) {
|
|
80
|
+
// DEVNOTE: 2019/7/23 15:01:48 - types
|
|
81
|
+
return data.concat(remData);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
remData && Object.assign(data, remData);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return data;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* @import {
|
|
91
|
+
* TESIErrorStats,
|
|
92
|
+
* TESIErrorStatMap,
|
|
93
|
+
* TESIErrorWithStat
|
|
94
|
+
* } from "./esi-error-types";
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* #### status: (400 | 401 | 403 | 404 | 420 | 422) or (500 | 503 | 504 | 520)
|
|
98
|
+
*
|
|
99
|
+
* @param {Response} res
|
|
100
|
+
* @param {string} endpointUrl
|
|
101
|
+
* @param {AbortController} abortable
|
|
102
|
+
* @returns {Promise<void>}
|
|
103
|
+
*/
|
|
104
|
+
export const handleESIError = async (res, endpointUrl, abortable) => {
|
|
105
|
+
const status = /** @type {TESIErrorStats} */ (res.status);
|
|
106
|
+
/** @type {TESIErrorStatMap[typeof status]} */
|
|
107
|
+
const esiError = await res.json();
|
|
108
|
+
/** @type {TESIErrorWithStat<typeof status>} */
|
|
109
|
+
const errorType = {
|
|
110
|
+
status, ...esiError
|
|
111
|
+
};
|
|
112
|
+
// log ESI Error details
|
|
113
|
+
console.warn(errorType);
|
|
114
|
+
if (status === 420) {
|
|
115
|
+
abortable && abortable.abort();
|
|
116
|
+
throw new ESIErrorLimitReachedError();
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// console.log(res);
|
|
120
|
+
throw new ESIRequesError(`${res.statusText} (status=${status}, url=${endpointUrl})`);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
/** @satisfies {TESIErrorStats[]} */
|
|
124
|
+
const ESIErrorStats = [
|
|
125
|
+
// status 400
|
|
126
|
+
400, // BadRequest;
|
|
127
|
+
401, // Unauthorized;
|
|
128
|
+
403, // Forbidden;
|
|
129
|
+
404, // NotFound;
|
|
130
|
+
420, // ErrorLimited;
|
|
131
|
+
422, // Unprocessable;
|
|
132
|
+
// status 500
|
|
133
|
+
500, // InternalServerError;
|
|
134
|
+
503, // ServiceUnavailable;
|
|
135
|
+
504, // GatewayTimeout;
|
|
136
|
+
520, // EVEServerError;
|
|
137
|
+
];
|
|
138
|
+
/**
|
|
139
|
+
* @param {number} stat
|
|
140
|
+
* @returns {stat is 200 | 201 | 204}
|
|
141
|
+
*/
|
|
142
|
+
export const isSuccess = (stat) => {
|
|
143
|
+
return stat === 200 || stat === 201 || stat === 204;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* @param {number} stat
|
|
147
|
+
* @returns {stat is TESIErrorStats}
|
|
148
|
+
*/
|
|
149
|
+
export const isError = (stat) => {
|
|
150
|
+
return ESIErrorStats.includes(/** @type {TESIErrorStats} */ (stat));
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* @returns {boolean}
|
|
154
|
+
*/
|
|
49
155
|
export const isDebug = () => {
|
|
50
156
|
return process.argv.includes("-debug");
|
|
51
157
|
};
|
|
158
|
+
/**
|
|
159
|
+
* @param {string} opt
|
|
160
|
+
* @returns {boolean}
|
|
161
|
+
*/
|
|
52
162
|
export const is = (opt) => process.argv.includes(`-${opt}`);
|
|
53
163
|
/**
|
|
54
164
|
* @param {string} method
|
|
55
165
|
* @param {ESIRequestOptions} opt
|
|
56
|
-
* @returns
|
|
166
|
+
* @returns {{ rqopt: RequestInit, qss: Record<string, string> }}
|
|
57
167
|
*/
|
|
58
168
|
export const initOptions = (method, opt) => {
|
|
59
169
|
/** @type {RequestInit} */
|
|
@@ -64,6 +174,7 @@ export const initOptions = (method, opt) => {
|
|
|
64
174
|
signal: opt.cancelable?.signal,
|
|
65
175
|
headers: {}
|
|
66
176
|
};
|
|
177
|
+
/** @type {Record<string, string>} */
|
|
67
178
|
const qss = {
|
|
68
179
|
// language: "en",
|
|
69
180
|
};
|
|
@@ -87,12 +198,13 @@ export const initOptions = (method, opt) => {
|
|
|
87
198
|
* fetch the extra pages
|
|
88
199
|
*
|
|
89
200
|
* + if the `x-pages` header property ware more than 1
|
|
90
|
-
*
|
|
201
|
+
* @template {any} T
|
|
91
202
|
* @param {string} endpointUrl
|
|
92
203
|
* @param {RequestInit} rqopt request options
|
|
93
204
|
* @param {URLSearchParams} rqp queries
|
|
94
205
|
* @param {number} pc pageCount
|
|
95
206
|
* @param {(minus?: number) => void=} increment
|
|
207
|
+
* @returns {Promise<T | null>}
|
|
96
208
|
*/
|
|
97
209
|
export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { }) => {
|
|
98
210
|
const rqs = [];
|
|
@@ -113,7 +225,7 @@ export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { })
|
|
|
113
225
|
for (let i = 0, end = jsons.length; i < end;) {
|
|
114
226
|
combined = combined.concat(jsons[i++]);
|
|
115
227
|
}
|
|
116
|
-
return combined;
|
|
228
|
+
return /** @type {T} */ (combined);
|
|
117
229
|
}
|
|
118
230
|
LOG && log("> > > pages result are object < < < --", jsons);
|
|
119
231
|
return null;
|
|
@@ -128,22 +240,17 @@ export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { })
|
|
|
128
240
|
*
|
|
129
241
|
* @param {string} endpoint e.g - "/characters/{character_id}/"
|
|
130
242
|
* @param {number[]} ids
|
|
131
|
-
* @returns fragment of qualified endpoint uri or null.
|
|
243
|
+
* @returns {string | null} fragment of qualified endpoint uri or null.
|
|
132
244
|
*/
|
|
133
245
|
export const replaceCbt = (endpoint, ids) => {
|
|
134
|
-
const re = /{([\w]+)}/g;
|
|
135
|
-
/** @type {RegExpExecArray?} */
|
|
136
|
-
let m;
|
|
137
246
|
let idx = 0;
|
|
138
|
-
|
|
139
|
-
endpoint = endpoint.replace(m[0], ids[idx++] + "");
|
|
140
|
-
}
|
|
141
|
-
return endpoint;
|
|
247
|
+
return endpoint.replace(/{([\w]+)}/g, $0 => ids[idx++] + "");
|
|
142
248
|
};
|
|
143
249
|
/**
|
|
144
250
|
*
|
|
145
251
|
* @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
|
|
146
252
|
* + The version parameter is forced to apply `latest`
|
|
253
|
+
* @returns {string}
|
|
147
254
|
*/
|
|
148
255
|
export const curl = (endp) => {
|
|
149
256
|
endp = endp.replace(/^\/+|\/+$/g, "");
|
|
@@ -160,7 +267,7 @@ export async function getSDEVersion() {
|
|
|
160
267
|
const res = await fetch(sdeZipUrl, { method: "head", mode: "cors" });
|
|
161
268
|
const date = res.headers.get("last-modified");
|
|
162
269
|
if (date) {
|
|
163
|
-
const YMD = new Date(date).toLocaleDateString(
|
|
270
|
+
const YMD = new Date(date).toLocaleDateString("en-CA").replace(/-/g, "");
|
|
164
271
|
return `sde-${YMD}-TRANQUILITY`;
|
|
165
272
|
}
|
|
166
273
|
else {
|
|
@@ -175,7 +282,7 @@ export async function getSDEVersion() {
|
|
|
175
282
|
}
|
|
176
283
|
export function getLogger() {
|
|
177
284
|
const clog = console.log.bind(console, '- - -> Get the character data of "CCP Zoetrope"'.magenta);
|
|
178
|
-
const rlog = console.log.bind(console,
|
|
285
|
+
const rlog = console.log.bind(console, "- - -> Run ESI request".cyan);
|
|
179
286
|
return { clog, rlog };
|
|
180
287
|
}
|
|
181
288
|
/**
|
|
@@ -212,6 +319,7 @@ function fireWithoutAuth(fn, method, endpoint, pathParams, opt) {
|
|
|
212
319
|
* #### Fire a request that does not require authentication.
|
|
213
320
|
*
|
|
214
321
|
* @param {TESIRequestFunctionSignature<ESIRequestOptions> | TESIRequestFunctionMethods} fn
|
|
322
|
+
* @returns {Promise<void>}
|
|
215
323
|
*/
|
|
216
324
|
export async function fireRequestsDoesNotRequireAuth(fn) {
|
|
217
325
|
const { clog, rlog } = getLogger();
|
|
@@ -278,6 +386,7 @@ export async function fireRequestsDoesNotRequireAuth(fn) {
|
|
|
278
386
|
},
|
|
279
387
|
});
|
|
280
388
|
// TODO: want TypeScript semantics to throw an error because there is a required query parameter, but it's not possible
|
|
389
|
+
// Or rather, I don't know how to do it.
|
|
281
390
|
await fireWithoutAuth(fn, "get", "/characters/{character_id}/search/");
|
|
282
391
|
log(ok);
|
|
283
392
|
}
|
|
@@ -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<
|
|
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:
|
|
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
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
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
|
|
54
|
-
const
|
|
55
|
-
/** @type {
|
|
56
|
-
const entry = /** @type {XESI.ESITaggedEndpointRequest<typeof tag>} */ ({});
|
|
57
|
-
for (const method of
|
|
58
|
-
// @ts-
|
|
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
|
-
|
|
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
|
/**
|
package/package.json
CHANGED
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
package/v2/esi-tagged-types.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
package/v2/response-map.d.ts
CHANGED
|
@@ -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.
|
|
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
|
|
package/v2/types-index.d.ts
CHANGED
|
@@ -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.
|
|
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,
|
|
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
|
// - - - - - - - - - - - - - - - - - - - -
|
|
@@ -64,52 +68,16 @@ export async function fire(mthd, endp, pathParams, opt) {
|
|
|
64
68
|
try {
|
|
65
69
|
const res = await fetch(url, rqopt).finally(() => ax--);
|
|
66
70
|
const { status } = res;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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;
|
|
71
|
+
// The parameters are different for successful and error responses.
|
|
72
|
+
if (isSuccess(status)) {
|
|
73
|
+
return handleSuccessResponse(res, endpointUrl, rqopt, up, incrementAx);
|
|
108
74
|
}
|
|
75
|
+
// else if (isError(status)) {}
|
|
76
|
+
// Actually, throw Error
|
|
77
|
+
throw await handleESIError(res, endpointUrl, actualOpt.cancelable);
|
|
109
78
|
}
|
|
110
79
|
catch (e) {
|
|
111
|
-
|
|
112
|
-
throw new ESIRequesError(`message: ${e.message}, endpoint=${endp}`);
|
|
80
|
+
throw e;
|
|
113
81
|
}
|
|
114
82
|
}
|
|
115
83
|
// It should complete correctly.
|