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 +5 -1
- package/index.html +18 -0
- package/lib/esi-error-types.d.ts +104 -0
- package/lib/request-api.mjs +8 -8
- package/lib/rq-util.d.mts +76 -12
- package/lib/rq-util.mjs +180 -21
- package/lib/tagged-request-api.d.mts +8 -2
- package/lib/tagged-request-api.mjs +35 -30
- package/minimal-rq.mjs +9 -4
- package/package.json +2 -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 -47
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
|
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 {}
|
package/lib/request-api.mjs
CHANGED
|
@@ -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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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
|
-
* @
|
|
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:
|
|
159
|
+
export declare const replaceCbt: <T extends unknown>(endpoint: T, ids: number[]) => T;
|
|
99
160
|
/**
|
|
100
161
|
*
|
|
101
|
-
* @
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
* @
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
return endpoint;
|
|
295
|
+
// @ts-expect-error
|
|
296
|
+
return endpoint.replace(/{([\w]+)}/g, $0 => ids[idx++] + "");
|
|
142
297
|
};
|
|
143
298
|
/**
|
|
144
299
|
*
|
|
145
|
-
* @
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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<
|
|
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
|
/**
|
|
@@ -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.
|
|
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
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
|
// - - - - - - - - - - - - - - - - - - - -
|
|
@@ -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
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
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
|
-
|
|
112
|
-
throw new ESIRequesError(`message: ${e.message}, endpoint=${endp}`);
|
|
77
|
+
throw e;
|
|
113
78
|
}
|
|
114
79
|
}
|
|
115
80
|
// It should complete correctly.
|