eve-esi-types 2.3.2 → 2.3.4

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.
@@ -0,0 +1,15 @@
1
+ declare global {
2
+ interface String {
3
+ red: string;
4
+ green: string;
5
+ yellow: string;
6
+ blue: string;
7
+ magenta: string;
8
+ cyan: string;
9
+ }
10
+ }
11
+ /**
12
+ * @param {boolean} enable
13
+ */
14
+ export declare function defineColors(enable: boolean): void;
15
+ export declare function getLogger(banner: string, logSelector?: string): (...args: any[]) => void;
@@ -0,0 +1,102 @@
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/lib/console-util.mts
10
+ */
11
+ import { isNode } from "./constants.mjs";
12
+ let colorsDefined = false;
13
+ /**
14
+ * @param {boolean} enable
15
+ */
16
+ export function defineColors(enable) {
17
+ if (isNode)
18
+ return;
19
+ if (!colorsDefined) {
20
+ colorsDefined = true;
21
+ Object.defineProperties(String.prototype, {
22
+ red: {
23
+ get() { return enable ? `<span style="color: red">${this}</span>` : this; }
24
+ },
25
+ green: {
26
+ get() { return enable ? `<span style="color: green">${this}</span>` : this; }
27
+ },
28
+ yellow: {
29
+ get() { return enable ? `<span style="color: yellow">${this}</span>` : this; }
30
+ },
31
+ blue: {
32
+ get() { return enable ? `<span style="color: blue">${this}</span>` : this; }
33
+ },
34
+ magenta: {
35
+ get() { return enable ? `<span style="color: #FF00FF">${this}</span>` : this; }
36
+ },
37
+ cyan: {
38
+ get() { return enable ? `<span style="color: #00FFFF">${this}</span>` : this; }
39
+ },
40
+ });
41
+ }
42
+ }
43
+ /**
44
+ * @param {string} jsonString
45
+ */
46
+ const jsonBeautify = (jsonString) => {
47
+ const re = /("(?:[^"\\]|\\.)*"(?:\s*:)?)|(-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)|\b(true|false)\b|\b(null)\b/gm;
48
+ return jsonString.replace(re, (m, key_or_str, num, bool, nul) => {
49
+ if (key_or_str) { // key
50
+ let clz;
51
+ const last = m.length - 1;
52
+ if (m[last] === ":") {
53
+ m = m.substring(0, last);
54
+ clz = "key";
55
+ }
56
+ else {
57
+ clz = "string";
58
+ }
59
+ let tag = `<span class="${clz}">${m}</span>`;
60
+ clz === "key" && (tag += ":");
61
+ return tag;
62
+ }
63
+ if (num) { // number
64
+ return `<span class="number">${m}</span>`;
65
+ }
66
+ if (bool) { // boolean
67
+ return `<span class="bool">${m}</span>`;
68
+ }
69
+ if (nul) { // null
70
+ return `<span class="nul">${m}</span>`;
71
+ }
72
+ return m;
73
+ });
74
+ };
75
+ export function getLogger(banner, logSelector = ".log-frame") {
76
+ /** @type {Element=} */
77
+ const logElement = isNode ? void 0 : document.querySelector(logSelector) || void 0;
78
+ defineColors(!!logElement);
79
+ if (!logElement) {
80
+ return console.log.bind(console, banner);
81
+ }
82
+ else {
83
+ /** @type {function(...any): void} */
84
+ const log = (...data) => {
85
+ let text = banner;
86
+ for (const d of data) {
87
+ if (typeof d === "object") {
88
+ const json = JSON.stringify(d, null, 2);
89
+ text += jsonBeautify(json);
90
+ }
91
+ else {
92
+ text += " " + d;
93
+ }
94
+ }
95
+ requestAnimationFrame(() => {
96
+ logElement.innerHTML += text + "\n";
97
+ logElement.scrollTop = 999999;
98
+ });
99
+ };
100
+ return log;
101
+ }
102
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Dummy import
3
+ * @import * as cc from "colors.ts";
4
+ */
5
+ /**
6
+ * will almost certainly be able to detect if you are in a nodejs environment
7
+ *
8
+ * @date 2020/5/9
9
+ */
10
+ export declare const isNode: boolean;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Dummy import
3
+ * @import * as cc from "colors.ts";
4
+ */
5
+ /**
6
+ * will almost certainly be able to detect if you are in a nodejs environment
7
+ *
8
+ * @date 2020/5/9
9
+ */
10
+ export const isNode = await (async () => {
11
+ let returnValue;
12
+ if (typeof process === "object") {
13
+ returnValue = typeof process.versions === "object" && /\d+\.\d+\.\d+/.test(process.versions.node);
14
+ if (returnValue) {
15
+ await import("colors.ts");
16
+ }
17
+ }
18
+ else {
19
+ // modId = "https://cdn.jsdelivr.net/npm/colors.ts@1.0.20/+esm";
20
+ returnValue = false;
21
+ }
22
+ return returnValue;
23
+ })();
@@ -1,3 +1,14 @@
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/lib/esi-error-types.d.ts
10
+ * @since 2.3.2
11
+ */
1
12
 
2
13
  type TEVEErrorBase = {
3
14
  /**
@@ -34,8 +45,9 @@ export type TESIErrorStatMap = {
34
45
  * const res = await fetch("<URL with ESI endpoint>");
35
46
  * const status = res.status;
36
47
  * if (status >= 400) {
48
+ * const esiError = await res.json() as TESIErrorStatMap[typeof status];
37
49
  * const errorType: TESIErrorWithStat<typeof status> = {
38
- * status, ...res.json()
50
+ * status, ...esiError
39
51
  * };
40
52
  * console.log(errorType);
41
53
  * throw new Error(`message="${res.statusText}", status=${status}`);
@@ -43,7 +55,6 @@ export type TESIErrorStatMap = {
43
55
  * ```
44
56
  *
45
57
  * @date 2025/3/3
46
- * @since 2.4.0
47
58
  */
48
59
  export type TESIErrorWithStat<Stat extends TClientErrorStats | TServerErrorStats> = {
49
60
  status: Stat;
@@ -14,7 +14,7 @@ import * as util from "./rq-util.mjs";
14
14
  // constants, types
15
15
  // - - - - - - - - - - - - - - - - - - - -
16
16
  // shorthands
17
- const log = console.log;
17
+ const log = util.getUniversalLogger("[request-api]: ");
18
18
  const DEBUG = util.isDebug();
19
19
  /**
20
20
  * @typedef {import("../v2").IESIRequestFunction<util.ESIRequestOptions>} IESIRequestFunction
@@ -36,29 +36,24 @@ const DEBUG = util.isDebug();
36
36
  * @returns A Promise object containing the response data
37
37
  * @throws {ESIRequesError}
38
38
  */
39
- // @ts-expect-error
40
- export const request = async (method, endpoint, pathParams, opt) => {
39
+ export const request = /** @type {IESIRequestFunction} */ (async (method, endpoint, pathParams, opt) => {
41
40
  if (typeof pathParams === "number") {
42
- // @ts-expect-error
43
- pathParams = [pathParams];
41
+ pathParams = /** @type {typeof pathParams} */ ([pathParams]);
44
42
  }
45
43
  if (Array.isArray(pathParams)) {
46
- // @ts-expect-error actualy endp is string
47
44
  endpoint = util.replaceCbt(endpoint, pathParams);
48
45
  }
49
46
  // When only options are provided
50
47
  const actualOpt = /** @type {NonNullable<typeof opt>} */ (opt || pathParams || {});
51
48
  const { rqopt, qss } = util.initOptions(method, actualOpt);
52
- // @ts-expect-error actualy endp is string
53
49
  const endpointUrl = util.curl(endpoint);
54
50
  const up = new URLSearchParams(qss);
55
51
  const url = `${endpointUrl}${up.size ? `?${up}` : ""}`;
56
52
  DEBUG && log(url);
57
53
  try {
58
54
  const res = await fetch(url, rqopt);
59
- const { status } = res;
60
55
  // The parameters are different for successful and error responses.
61
- if (util.isSuccess(status)) {
56
+ if (util.isSuccess(res.status)) {
62
57
  return util.handleSuccessResponse(res, endpointUrl, rqopt, up);
63
58
  }
64
59
  // else if (isError(status)) {}
@@ -68,7 +63,7 @@ export const request = async (method, endpoint, pathParams, opt) => {
68
63
  catch (e) {
69
64
  log(e);
70
65
  }
71
- };
66
+ });
72
67
  //
73
68
  // implements rest methods of `request` (IESIRequestFunction)
74
69
  //
package/lib/rq-util.d.mts CHANGED
@@ -5,9 +5,20 @@
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
+ // - - - - - - - - - - - - - - - - - - - -
12
+ /*!
13
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
14
+ // Copyright (C) 2025 jeffy-g <hirotom1107@gmail.com>
15
+ // Released under the MIT license
16
+ // https://opensource.org/licenses/mit-license.php
17
+ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
18
+ */
9
19
  import type { TESIRequestFunctionMethods } from "../v2";
10
20
  import type { TESIErrorStats } from "./esi-error-types";
21
+ export { isNode } from "./constants.mjs";
11
22
  /**
12
23
  * this always `https://esi.evetech.net`
13
24
  */
@@ -86,7 +97,7 @@ export declare const handleSuccessResponse: (response: Response, endpointUrl: st
86
97
  *
87
98
  * @param {Response} res
88
99
  * @param {string} endpointUrl
89
- * @param {AbortController} abortable
100
+ * @param {AbortController=} abortable
90
101
  * @returns {Promise<void>}
91
102
  */
92
103
  export declare const handleESIError: (res: Response, endpointUrl: string, abortable?: AbortController) => Promise<void>;
@@ -110,13 +121,15 @@ export declare const isDebug: () => boolean;
110
121
  */
111
122
  export declare const is: (opt: string) => boolean;
112
123
  /**
124
+ * NOTE: In `initOptions`, if `auth=true`, then `token` can be set to a valid `accessToken` to successfully complete an authenticated request.
125
+ *
113
126
  * @param {string} method
114
127
  * @param {ESIRequestOptions} opt
115
- * @returns {{ rqopt: RequestInit, qss: Record<string, string> }}
128
+ * @returns {{ rqopt: RequestInit, qss: Record<string, any> }}
116
129
  */
117
130
  export declare const initOptions: (method: string, opt: ESIRequestOptions) => {
118
131
  rqopt: RequestInit;
119
- qss: Record<string, string>;
132
+ qss: Record<string, any>;
120
133
  };
121
134
  /**
122
135
  * fetch the extra pages
@@ -125,12 +138,12 @@ export declare const initOptions: (method: string, opt: ESIRequestOptions) => {
125
138
  * @template {any} T
126
139
  * @param {string} endpointUrl
127
140
  * @param {RequestInit} rqopt request options
128
- * @param {URLSearchParams} rqp queries
141
+ * @param {URLSearchParams} usp queries
129
142
  * @param {number} pc pageCount
130
143
  * @param {(minus?: number) => void=} increment
131
144
  * @returns {Promise<T | null>}
132
145
  */
133
- export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: RequestInit, rqp: URLSearchParams, pc: number, increment?: (minus?: Truthy) => void) => Promise<T | null>;
146
+ export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: RequestInit, usp: URLSearchParams, pc: number, increment?: (minus?: Truthy) => void) => Promise<T | null>;
134
147
  /** ### replace (C)urly (B)races (T)oken
135
148
  *
136
149
  * @example
@@ -138,24 +151,26 @@ export declare const fetchP: <T extends unknown>(endpointUrl: string, rqopt: Req
138
151
  * // ->
139
152
  * "/characters/<char.character_id>/skills"
140
153
  *
141
- * @param {string} endpoint e.g - "/characters/{character_id}/"
154
+ * @template {unknown} T
155
+ * @param {T} endpoint e.g - "/characters/{character_id}/"
142
156
  * @param {number[]} ids
143
- * @returns {string | null} fragment of qualified endpoint uri or null.
157
+ * @returns {T} fragment of qualified endpoint uri or null.
144
158
  */
145
- export declare const replaceCbt: (endpoint: string, ids: number[]) => string;
159
+ export declare const replaceCbt: <T extends unknown>(endpoint: T, ids: number[]) => T;
146
160
  /**
147
- *
148
- * @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
161
+ * @template {unknown} T
162
+ * @param {T} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
149
163
  * + The version parameter is forced to apply `latest`
150
164
  * @returns {string}
151
165
  */
152
- export declare const curl: (endp: string) => string;
166
+ export declare const curl: <T extends unknown>(endp: T) => string;
153
167
  /**
154
168
  * @date 2020/03/31
155
169
  * @version 2.1
156
170
  * @type {() => Promise<string>}
157
171
  */
158
172
  export declare function getSDEVersion(): Promise<string>;
173
+ export declare const getUniversalLogger: (banner: string, logSelector?: string) => (...args: any[]) => void;
159
174
  export declare function getLogger(): {
160
175
  clog: (...args: any[]) => void;
161
176
  rlog: (...args: any[]) => void;
package/lib/rq-util.mjs CHANGED
@@ -9,12 +9,14 @@
9
9
  // - - - - - - - - - - - - - - - - - - - -
10
10
  // imports
11
11
  // - - - - - - - - - - - - - - - - - - - -
12
- import "colors.ts";
12
+ import { isNode } from "./constants.mjs";
13
+ export { isNode } from "./constants.mjs";
14
+ import * as consoleUtil from "./console-util.mjs";
13
15
  // - - - - - - - - - - - - - - - - - - - -
14
16
  // constants, types
15
17
  // - - - - - - - - - - - - - - - - - - - -
16
18
  // shorthands
17
- const log = console.log;
19
+ const log = consoleUtil.getLogger("[request-util]:");
18
20
  const isArray = Array.isArray;
19
21
  /**
20
22
  * enable/disable console.log
@@ -98,7 +100,7 @@ export const handleSuccessResponse = async (response, endpointUrl, requestOpt, u
98
100
  *
99
101
  * @param {Response} res
100
102
  * @param {string} endpointUrl
101
- * @param {AbortController} abortable
103
+ * @param {AbortController=} abortable
102
104
  * @returns {Promise<void>}
103
105
  */
104
106
  export const handleESIError = async (res, endpointUrl, abortable) => {
@@ -110,13 +112,12 @@ export const handleESIError = async (res, endpointUrl, abortable) => {
110
112
  status, ...esiError
111
113
  };
112
114
  // log ESI Error details
113
- console.warn(errorType);
115
+ log(errorType);
114
116
  if (status === 420) {
115
117
  abortable && abortable.abort();
116
118
  throw new ESIErrorLimitReachedError();
117
119
  }
118
120
  else {
119
- // console.log(res);
120
121
  throw new ESIRequesError(`${res.statusText} (status=${status}, url=${endpointUrl})`);
121
122
  }
122
123
  };
@@ -153,17 +154,42 @@ export const isError = (stat) => {
153
154
  * @returns {boolean}
154
155
  */
155
156
  export const isDebug = () => {
156
- return process.argv.includes("-debug");
157
+ return is("debug");
157
158
  };
158
159
  /**
159
160
  * @param {string} opt
160
161
  * @returns {boolean}
161
162
  */
162
- export const is = (opt) => process.argv.includes(`-${opt}`);
163
+ export const is = (opt) => {
164
+ if (isNode) {
165
+ return process.argv.includes(`-${opt}`);
166
+ }
167
+ else {
168
+ const q = location.search || location.hash;
169
+ if (q) {
170
+ //* ctt
171
+ const entries = q.substring(1).split("&");
172
+ for (const entry of entries) {
173
+ const [key, /*value*/] = entry.split("=");
174
+ if (key === opt)
175
+ return true;
176
+ }
177
+ /*/
178
+ const usp = new URLSearchParams(q.substring(1));
179
+ for (const [key, value] of usp.entries()) {
180
+ if (key === opt) return true;
181
+ }
182
+ //*/
183
+ }
184
+ }
185
+ return false;
186
+ };
163
187
  /**
188
+ * NOTE: In `initOptions`, if `auth=true`, then `token` can be set to a valid `accessToken` to successfully complete an authenticated request.
189
+ *
164
190
  * @param {string} method
165
191
  * @param {ESIRequestOptions} opt
166
- * @returns {{ rqopt: RequestInit, qss: Record<string, string> }}
192
+ * @returns {{ rqopt: RequestInit, qss: Record<string, any> }}
167
193
  */
168
194
  export const initOptions = (method, opt) => {
169
195
  /** @type {RequestInit} */
@@ -174,14 +200,17 @@ export const initOptions = (method, opt) => {
174
200
  signal: opt.cancelable?.signal,
175
201
  headers: {}
176
202
  };
177
- /** @type {Record<string, string>} */
203
+ /** @type {Record<string, any>} */
178
204
  const qss = {
179
- // language: "en",
205
+ // CAVEAT: If the language parameter is not set, some endpoints such as "/universe/ids/" may return incomplete results.
206
+ // Therefore, the language parameter should always be set.
207
+ language: "en",
180
208
  };
181
209
  if (opt.query) {
182
210
  // Object.assign(query, options.query); Object.assign is too slow
183
211
  const oqs = opt.query;
184
- Object.keys(oqs).forEach(k => qss[k] = oqs[k]);
212
+ for (const key in oqs)
213
+ qss[key] = oqs[key];
185
214
  }
186
215
  if (opt.auth) {
187
216
  // @ts-ignore The header is indeed an object
@@ -201,17 +230,17 @@ export const initOptions = (method, opt) => {
201
230
  * @template {any} T
202
231
  * @param {string} endpointUrl
203
232
  * @param {RequestInit} rqopt request options
204
- * @param {URLSearchParams} rqp queries
233
+ * @param {URLSearchParams} usp queries
205
234
  * @param {number} pc pageCount
206
235
  * @param {(minus?: number) => void=} increment
207
236
  * @returns {Promise<T | null>}
208
237
  */
209
- export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { }) => {
238
+ export const fetchP = async (endpointUrl, rqopt, usp, pc, increment = () => { }) => {
210
239
  const rqs = [];
211
240
  for (let i = 2; i <= pc;) {
212
- rqp.set("page", (i++) + "");
241
+ usp.set("page", (i++) + "");
213
242
  increment();
214
- rqs.push(fetch(`${endpointUrl}?${rqp + ""}`, rqopt).then(res => res.json()).catch(reason => {
243
+ rqs.push(fetch(`${endpointUrl}?${usp + ""}`, rqopt).then(res => res.json()).catch(reason => {
215
244
  console.warn(reason);
216
245
  return [];
217
246
  }).finally(() => {
@@ -238,23 +267,25 @@ export const fetchP = async (endpointUrl, rqopt, rqp, pc, increment = () => { })
238
267
  * // ->
239
268
  * "/characters/<char.character_id>/skills"
240
269
  *
241
- * @param {string} endpoint e.g - "/characters/{character_id}/"
270
+ * @template {unknown} T
271
+ * @param {T} endpoint e.g - "/characters/{character_id}/"
242
272
  * @param {number[]} ids
243
- * @returns {string | null} fragment of qualified endpoint uri or null.
273
+ * @returns {T} fragment of qualified endpoint uri or null.
244
274
  */
245
275
  export const replaceCbt = (endpoint, ids) => {
246
276
  let idx = 0;
277
+ // @ts-expect-error
247
278
  return endpoint.replace(/{([\w]+)}/g, $0 => ids[idx++] + "");
248
279
  };
249
280
  /**
250
- *
251
- * @param {string} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
281
+ * @template {unknown} T
282
+ * @param {T} endp this means endpoint url fragment like `/characters/{character_id}/` or `/characters/{character_id}/agents_research/`
252
283
  * + The version parameter is forced to apply `latest`
253
284
  * @returns {string}
254
285
  */
255
286
  export const curl = (endp) => {
256
- endp = endp.replace(/^\/+|\/+$/g, "");
257
- return `${BASE}/latest/${endp}/`;
287
+ // @ts-expect-error
288
+ return `${BASE}/latest/${endp.replace(/^\/+|\/+$/g, "")}/`;
258
289
  };
259
290
  /**
260
291
  * @date 2020/03/31
@@ -280,9 +311,12 @@ export async function getSDEVersion() {
280
311
  return "sde-202Xxxxx-TRANQUILITY";
281
312
  }
282
313
  }
314
+ export const getUniversalLogger = (banner, logSelector = ".log-frame") => {
315
+ return consoleUtil.getLogger(banner, logSelector);
316
+ };
283
317
  export function getLogger() {
284
- const clog = console.log.bind(console, '- - -> Get the character data of "CCP Zoetrope"'.magenta);
285
- const rlog = console.log.bind(console, "- - -> Run ESI request".cyan);
318
+ const clog = consoleUtil.getLogger('- - -> Get the character data of "CCP Zoetrope"'.magenta);
319
+ const rlog = consoleUtil.getLogger("- - -> Run ESI request".cyan);
286
320
  return { clog, rlog };
287
321
  }
288
322
  /**
@@ -314,7 +348,6 @@ function fireWithoutAuth(fn, method, endpoint, pathParams, opt) {
314
348
  }
315
349
  return fn[method](endpoint, pathParams, opt);
316
350
  }
317
- // It should complete correctly.
318
351
  /**
319
352
  * #### Fire a request that does not require authentication.
320
353
  *
@@ -323,17 +356,18 @@ function fireWithoutAuth(fn, method, endpoint, pathParams, opt) {
323
356
  */
324
357
  export async function fireRequestsDoesNotRequireAuth(fn) {
325
358
  const { clog, rlog } = getLogger();
359
+ const ID_CCP_Zoetrope = 2112625428;
326
360
  try {
327
361
  // - - - - - - - - - - - -
328
362
  // Character
329
363
  // - - - - - - - - - - - -
330
364
  // Here, I borrow data from "CCP Zoetrope".
331
365
  clog();
332
- await fireWithoutAuth(fn, "get", "/characters/{character_id}/", 2112625428).then(log);
366
+ await fireWithoutAuth(fn, "get", "/characters/{character_id}/", ID_CCP_Zoetrope).then(log);
333
367
  clog('(portrait)');
334
- await fireWithoutAuth(fn, "get", "/characters/{character_id}/portrait/", 2112625428).then(log);
368
+ await fireWithoutAuth(fn, "get", "/characters/{character_id}/portrait/", ID_CCP_Zoetrope).then(log);
335
369
  clog('(affiliation)');
336
- const affiliation = await fireWithoutAuth(fn, "post", "/characters/affiliation/", { body: [2112625428] });
370
+ const affiliation = await fireWithoutAuth(fn, "post", "/characters/affiliation/", { body: [ID_CCP_Zoetrope] });
337
371
  log(affiliation);
338
372
  clog('(corporation)');
339
373
  await fireWithoutAuth(fn, "get", "/corporations/{corporation_id}/", affiliation[0].corporation_id).then(log);
@@ -346,6 +380,7 @@ export async function fireRequestsDoesNotRequireAuth(fn) {
346
380
  const ids = await fireWithoutAuth(fn, "post", "/universe/ids/", { body: ["the forge", "plex"] });
347
381
  log(ids.inventory_types, ids.regions);
348
382
  rlog(`get:/markets/${ids?.regions?.[0].id}/orders/?type_id=${ids?.inventory_types?.[0].id}, item PLEX`.green);
383
+ // in this case, "order_type" is required
349
384
  const orders = await fireWithoutAuth(fn, "get", "/markets/{region_id}/orders/", ids?.regions?.[0].id, {
350
385
  query: {
351
386
  // page: 1,
@@ -366,29 +401,23 @@ export async function fireRequestsDoesNotRequireAuth(fn) {
366
401
  // The following is code to observe the behavior of completion by generics.
367
402
  // Authentication is required, so an error will occur.
368
403
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
369
- let ok = await fireWithoutAuth(fn, "get", "/characters/{character_id}/ship/", 994562, {
404
+ let willFailed = await fireWithoutAuth(fn, "get", "/characters/{character_id}/ship/", ID_CCP_Zoetrope, {
370
405
  auth: true,
371
406
  ignoreError: true,
372
407
  token: "token.token.token"
373
408
  });
374
409
  // in this case, "categories" and "search" is required
375
- await fireWithoutAuth(fn, "get", "/characters/{character_id}/search/", 994562, {
410
+ await fireWithoutAuth(fn, "get", "/characters/{character_id}/search/", ID_CCP_Zoetrope, {
376
411
  query: {
377
412
  categories: ["agent"],
378
413
  search: "ok"
379
414
  },
380
415
  auth: true
381
416
  });
382
- // in this case, "order_type" is required
383
- await fireWithoutAuth(fn, "get", "/markets/{region_id}/orders/", 994562, {
384
- query: {
385
- order_type: "all"
386
- },
387
- });
388
417
  // TODO: want TypeScript semantics to throw an error because there is a required query parameter, but it's not possible
389
418
  // Or rather, I don't know how to do it.
390
419
  await fireWithoutAuth(fn, "get", "/characters/{character_id}/search/");
391
- log(ok);
420
+ log(willFailed);
392
421
  }
393
422
  catch (e) {
394
423
  console.error("Failed to request -", e);
@@ -18,9 +18,9 @@ import type { ESIRequestOptions } from "./rq-util.mjs";
18
18
  * const esiRequest = taggedApi.injectESIRequestBody(...);
19
19
  * const ret = await esiRequest.universe.get("/universe/structures/", { query: { filter: "market" }});
20
20
  *
21
- * @template {Record<string, unknown>} Opt
22
- * @param {TESIRequestFunctionSignature<{}>} requestBody
23
- * @returns {XESI.TaggedESIRequestMap}
21
+ * @template {Record<string, unknown>} Opt - The options type for the request.
22
+ * @param {TESIRequestFunctionSignature<Opt>} requestBody - The function signature for the ESI request.
23
+ * @returns {XESI.TaggedESIRequestMap} - The tagged ESI request map.
24
24
  * @since 2.3
25
25
  */
26
26
  export declare function injectESIRequestBody<Opt extends Record<string, unknown>>(requestBody: TESIRequestFunctionSignature<Opt>): XESI.TaggedESIRequestMap<Opt>;
@@ -29,6 +29,7 @@ export declare function injectESIRequestBody<Opt extends Record<string, unknown>
29
29
  */
30
30
  /**
31
31
  * Injects the minimal implementation of ESI requests into `XESI.TaggedESIRequestMap`.
32
+ *
32
33
  * @since 2.3
33
34
  * @type {XESI.TaggedESIRequestMap<ESIRequestOptions>}
34
35
  */
@@ -10,11 +10,14 @@
10
10
  * @file eve-esi-types/lib/tagged-request-api.mts
11
11
  */
12
12
  import { request } from "./request-api.mjs";
13
+ /**
14
+ * @import { TESIRequestFunctionMethods } from "../v2"
15
+ */
13
16
  /**
14
17
  * @typedef {`${string}${"" | `,${string}`}`} TMethodList
15
18
  */
16
19
  /** @satisfies {`${XESI.ESITags}:${TMethodList}`[]} */
17
- const ESITagsWithMethodList = [
20
+ const ESITagMethodMapping = [
18
21
  "Alliance:get", "Assets:get,post",
19
22
  "Calendar:get,put", "Character:get,post",
20
23
  "Clones:get", "Contacts:get,post,put,delete",
@@ -43,31 +46,39 @@ const ESITagsWithMethodList = [
43
46
  * const esiRequest = taggedApi.injectESIRequestBody(...);
44
47
  * const ret = await esiRequest.universe.get("/universe/structures/", { query: { filter: "market" }});
45
48
  *
46
- * @template {Record<string, unknown>} Opt
47
- * @param {TESIRequestFunctionSignature<{}>} requestBody
48
- * @returns {XESI.TaggedESIRequestMap}
49
+ * @template {Record<string, unknown>} Opt - The options type for the request.
50
+ * @param {TESIRequestFunctionSignature<Opt>} requestBody - The function signature for the ESI request.
51
+ * @returns {XESI.TaggedESIRequestMap} - The tagged ESI request map.
49
52
  * @since 2.3
50
53
  */
51
54
  export function injectESIRequestBody(requestBody) {
52
55
  const rq = /** @type {XESI.TaggedESIRequestMap<Opt>} */ ({});
53
- for (const tagEntry of ESITagsWithMethodList) {
56
+ // DEVNOTE: 2025/03/08 - In reality, you only need one function instance for each of "get", "post", "put", and "delete",
57
+ // so you can just refer to the cached functions as a map.
58
+ const methodMap = /** @type {TESIRequestFunctionMethods<Opt>} */ ({});
59
+ /** @type {TESIEntryMethod[]} */ (["get", "post", "put", "delete"]).forEach((method) => {
60
+ methodMap[method] = /** @type {TESIRequestFunctionEachMethod<typeof method, Opt>} */ ((e, params, opt) => requestBody(method, e, params, opt));
61
+ });
62
+ for (const tagEntry of ESITagMethodMapping) {
54
63
  const [tag, methodList] = /** @type {[tag: XESI.ESITags, methods: TMethodList]} */ (tagEntry.split(":"));
55
64
  const methods = /** @type {TESIEntryMethod[]} */ (methodList.split(","));
56
65
  const entry = /** @type {XESI.ESITaggedEndpointRequest<typeof tag, Opt>} */ ({});
57
66
  for (const method of methods) {
67
+ // However, from the point of view of type annotation, the parameters are different, so you need to cast it as `as type` instead of `satisfies`.
58
68
  // @ts-expect-error
59
- entry[method] = /** @satisfies {XESI.TaggedEndpointRequestFunction<typeof method, typeof tag>} */ ((e, params, opt) => requestBody(method, e, params, opt));
69
+ entry[method] = /** @type {XESI.TaggedEndpointRequestFunction<typeof method, typeof tag, Opt>} */ (methodMap[method]);
60
70
  }
61
71
  const camelCased = /** @type {XESI.LCamelCase<XESI.ESITags>} */ (tag[0].toLowerCase() + tag.slice(1).replace(/\s(.)/g, "$1"));
62
72
  rq[camelCased] = entry;
63
73
  }
64
- return rq;
74
+ return Object.freeze(rq);
65
75
  }
66
76
  /**
67
77
  * @import { ESIRequestOptions } from "./rq-util.mjs";
68
78
  */
69
79
  /**
70
80
  * Injects the minimal implementation of ESI requests into `XESI.TaggedESIRequestMap`.
81
+ *
71
82
  * @since 2.3
72
83
  * @type {XESI.TaggedESIRequestMap<ESIRequestOptions>}
73
84
  */
package/minimal-rq.mjs CHANGED
@@ -15,7 +15,7 @@ import { request } from "./lib/request-api.mjs";
15
15
  // constants, types
16
16
  // - - - - - - - - - - - - - - - - - - - -
17
17
  // shorthands
18
- const log = console.log;
18
+ const log = util.getUniversalLogger("[request-mini]: ");
19
19
  /**
20
20
  * @typedef {import("./v2").IESIRequestFunction<util.ESIRequestOptions>} IESIRequestFunction
21
21
  * @typedef {import("./v2").TESIRequestFunctionMethods<util.ESIRequestOptions>} TESIRequestFunctionMethods
@@ -44,17 +44,18 @@ async function getEVEStatus(fn) {
44
44
  }
45
45
  const { clog, rlog } = util.getLogger();
46
46
  CaseIESIRequestFunction: {
47
+ const ID_CCP_Zoetrope = 2112625428;
47
48
  // - - - - - - - - - - - -
48
49
  // Character
49
50
  // - - - - - - - - - - - -
50
51
  // Here, I borrow data from "CCP Zoetrope".
51
52
  rlog("- - - - - - - > run as IESIRequestFunction<ESIRequestOptions>".red);
52
53
  clog();
53
- await fn.get("/characters/{character_id}/", 2112625428).then(log);
54
+ await fn.get("/characters/{character_id}/", ID_CCP_Zoetrope).then(log);
54
55
  clog('(portrait)');
55
- await fn.get("/characters/{character_id}/portrait/", 2112625428).then(log);
56
+ await fn.get("/characters/{character_id}/portrait/", ID_CCP_Zoetrope).then(log);
56
57
  clog('(affiliation)');
57
- const affiliation = await fn.post("/characters/affiliation/", { body: [2112625428] });
58
+ const affiliation = await fn.post("/characters/affiliation/", { body: [ID_CCP_Zoetrope] });
58
59
  log(affiliation);
59
60
  clog('(corporation)');
60
61
  await fn.get("/corporations/{corporation_id}/", affiliation[0].corporation_id).then(log);
@@ -78,10 +79,23 @@ async function getEVEStatus(fn) {
78
79
  }
79
80
  return fn.get("/status/");
80
81
  }
82
+ const runTest = () => {
83
+ getEVEStatus(request).then(eveStatus => log(eveStatus));
84
+ };
81
85
  // type following and run
82
86
  // node minimal-rq.mjs -debug
83
87
  if (!util.is("x")) {
84
- getEVEStatus(request).then(eveStatus => log(eveStatus));
88
+ runTest();
89
+ }
90
+ else {
91
+ // @ts-ignore
92
+ globalThis.runTest = runTest;
93
+ if (!util.isNode) {
94
+ const button = document.getElementById("run-test");
95
+ if (button) {
96
+ button.addEventListener("click", () => runTest());
97
+ }
98
+ }
85
99
  }
86
100
  // {
87
101
  // "players": 16503,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve-esi-types",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
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": {
@@ -14,6 +14,7 @@
14
14
  "files": [
15
15
  "v2",
16
16
  "lib",
17
+ "web",
17
18
  "*.d.mts",
18
19
  "*.mjs",
19
20
  "LICENSE",
package/v2.mjs CHANGED
@@ -51,7 +51,6 @@ export async function fire(mthd, endp, pathParams, opt) {
51
51
  pathParams = [pathParams]; // as unknown as P2;
52
52
  }
53
53
  if (isArray(pathParams)) {
54
- // @ts-expect-error actualy endp is string
55
54
  endp = replaceCbt(endp, pathParams);
56
55
  }
57
56
  // When only options are provided
@@ -59,7 +58,6 @@ export async function fire(mthd, endp, pathParams, opt) {
59
58
  // @ts-ignore
60
59
  const actualOpt = opt || pathParams || {};
61
60
  const { rqopt, qss } = initOptions(mthd, actualOpt);
62
- // @ts-expect-error actualy endp is string
63
61
  const endpointUrl = curl(endp);
64
62
  const up = new URLSearchParams(qss);
65
63
  const url = `${endpointUrl}${up.size ? `?${up}` : ""}`;
@@ -67,9 +65,8 @@ export async function fire(mthd, endp, pathParams, opt) {
67
65
  ax++;
68
66
  try {
69
67
  const res = await fetch(url, rqopt).finally(() => ax--);
70
- const { status } = res;
71
68
  // The parameters are different for successful and error responses.
72
- if (isSuccess(status)) {
69
+ if (isSuccess(res.status)) {
73
70
  return handleSuccessResponse(res, endpointUrl, rqopt, up, incrementAx);
74
71
  }
75
72
  // else if (isError(status)) {}
package/web/index.css ADDED
@@ -0,0 +1,101 @@
1
+ @charset "utf8";
2
+ /*!
3
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
4
+ Copyright (C) 2025 jeffy-g <hirotom1107@gmail.com>
5
+ Released under the MIT license
6
+ https://opensource.org/licenses/mit-license.php
7
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
8
+ */
9
+
10
+ .command {
11
+ font-family: consolas;
12
+ color: rgb(37 143 231);
13
+ background-color: rgba(192, 192, 192, 0.23);
14
+ padding: 4px 6px;
15
+ border: solid 1px silver;
16
+ border-radius: 4px;
17
+ }
18
+
19
+ /* button {
20
+ width: 100px;
21
+ transition: filter 300ms ease;
22
+ } */
23
+ button:disabled {
24
+ filter: opacity(0.3) blur(0.4px);
25
+ }
26
+ button.copy {
27
+ width: 20px;
28
+ height: 20px;
29
+ background: url('data:image/svg+xml;charset=UTF-8,<svg height="16px" viewBox="-21 0 512 512" width="16px" xmlns="http://www.w3.org/2000/svg"><path d="m186.667969 416c-49.984375 0-90.667969-40.683594-90.667969-90.667969v-218.664062h-37.332031c-32.363281 0-58.667969 26.300781-58.667969 58.664062v288c0 32.363281 26.304688 58.667969 58.667969 58.667969h266.664062c32.363281 0 58.667969-26.304688 58.667969-58.667969v-37.332031zm0 0"/><path d="m469.332031 58.667969c0-32.40625-26.261719-58.667969-58.664062-58.667969h-224c-32.40625 0-58.667969 26.261719-58.667969 58.667969v266.664062c0 32.40625 26.261719 58.667969 58.667969 58.667969h224c32.402343 0 58.664062-26.261719 58.664062-58.667969zm0 0"/></svg>') no-repeat center/14px;
30
+ border: solid 1px rgba(192, 192, 192, 0.73);
31
+ border-radius: 3px;
32
+ display: inline-block;
33
+ vertical-align: middle;
34
+ margin-left: 3px;
35
+ cursor: pointer;
36
+ padding: 2px;
37
+ box-sizing: content-box;
38
+ }
39
+
40
+ .footer {
41
+ position: fixed;
42
+
43
+ box-sizing: border-box;
44
+ margin: 0;
45
+ left: 0; bottom: 2px;
46
+ width: 100%;
47
+ padding: 2px 8px;
48
+
49
+ display: flex;
50
+ justify-content: flex-end;
51
+ align-items: center;
52
+ align-content: center;
53
+ justify-items: end;
54
+
55
+ font-size: 10px;
56
+ background-color: var(--card-bg);
57
+ color: snow;
58
+ }
59
+ .footer a {
60
+ text-decoration: none;
61
+ margin: 0 4px;
62
+ }
63
+
64
+ .log-frame {
65
+ position: relative;
66
+
67
+ height: calc(100vh - 100px - 30px);
68
+
69
+ border: none;
70
+ padding: 3px 4px;
71
+ border-radius: 4px;
72
+
73
+ font-family: consolas;
74
+ white-space: pre;
75
+ overflow: auto;
76
+
77
+ background-color: rgb(22 22 22);
78
+ color: #ececec;
79
+ /* &:empty {
80
+ padding: 0;
81
+ width: 0; height: 0;
82
+ border: none;
83
+ border-radius: unset;
84
+ } */
85
+ }
86
+
87
+ span.key {
88
+ color: rgb(255 150 150);
89
+ }
90
+ span.number {
91
+ color: rgb(89 174 255);
92
+ }
93
+ span.string {
94
+ color: rgb(95 249 95);
95
+ }
96
+ span.bool {
97
+ color: rgb(205, 108, 255);
98
+ }
99
+ span.nul {
100
+ color: rgb(255, 44, 44);
101
+ }
package/web/index.html ADDED
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>eve-esi-types ESI request test</title>
8
+ <script type="module" src="../minimal-rq.mjs"></script>
9
+ <link rel="stylesheet" href="./index.css">
10
+ </head>
11
+
12
+ <body>
13
+ <div class="container">
14
+ <button id="run-test">Run test</button>
15
+ </div>
16
+ <div class="info">
17
+ <div>example command: <input type="text" class="command" value="browser-sync start -s --port 8080 --no-open --ss &lt;this site directory>" size="72" readonly>
18
+ <button class="copy" onclick="copy('.command', '.')"></button>
19
+ </div>
20
+ then access to <a href="http://localhost:8080/web/?x=1&debug=1">eve-esi-types ESI request test</a>
21
+ </div>
22
+ <div class="log-frame" contenteditable></div>
23
+ <footer class="footer"></footer>
24
+ <script>
25
+ /** @type {(s: string, rep: string, regex?: RegExp) => void} */
26
+ function copy(selector, replacement, regex = /<[^>]+>/) {
27
+ /** @type {HTMLInputElement} */
28
+ const text = document.querySelector(selector);
29
+ const shellscript = text.value.replace(regex, replacement);
30
+ navigator.clipboard.writeText(shellscript).then(() => alert("Copied the text: " + shellscript));
31
+ }
32
+ /** @type {HTMLDivElement} */
33
+ const logElement = document.querySelector(".log-frame");
34
+ if (logElement) {
35
+ let inputBuffer = "";
36
+ logElement.addEventListener("keydown", function (e) {
37
+ if (e.ctrlKey) {
38
+ if (e.code === "KeyL") {
39
+ e.preventDefault();
40
+ logElement.innerHTML = "";
41
+ }
42
+ } else {
43
+ const key = e.key;
44
+ if (key === "Enter") {
45
+ const cmd = inputBuffer;
46
+ inputBuffer = "";
47
+ eval(`(${cmd})`);
48
+ } else if (key.length === 1) {
49
+ inputBuffer += key;
50
+ }
51
+ }
52
+ });
53
+ }
54
+ </script>
55
+ </body>
56
+
57
+ </html>