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.
- package/lib/console-util.d.mts +15 -0
- package/lib/console-util.mjs +102 -0
- package/lib/constants.d.mts +10 -0
- package/lib/constants.mjs +23 -0
- package/lib/esi-error-types.d.ts +13 -2
- package/lib/request-api.mjs +5 -10
- package/lib/rq-util.d.mts +27 -12
- package/lib/rq-util.mjs +65 -36
- package/lib/tagged-request-api.d.mts +4 -3
- package/lib/tagged-request-api.mjs +18 -7
- package/minimal-rq.mjs +19 -5
- package/package.json +2 -1
- package/v2.mjs +1 -4
- package/web/index.css +101 -0
- package/web/index.html +57 -0
|
@@ -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,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
|
+
})();
|
package/lib/esi-error-types.d.ts
CHANGED
|
@@ -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, ...
|
|
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;
|
package/lib/request-api.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import * as util from "./rq-util.mjs";
|
|
|
14
14
|
// constants, types
|
|
15
15
|
// - - - - - - - - - - - - - - - - - - - -
|
|
16
16
|
// shorthands
|
|
17
|
-
const 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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}
|
|
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,
|
|
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
|
-
* @
|
|
154
|
+
* @template {unknown} T
|
|
155
|
+
* @param {T} endpoint e.g - "/characters/{character_id}/"
|
|
142
156
|
* @param {number[]} ids
|
|
143
|
-
* @returns {
|
|
157
|
+
* @returns {T} fragment of qualified endpoint uri or null.
|
|
144
158
|
*/
|
|
145
|
-
export declare const replaceCbt: (endpoint:
|
|
159
|
+
export declare const replaceCbt: <T extends unknown>(endpoint: T, ids: number[]) => T;
|
|
146
160
|
/**
|
|
147
|
-
*
|
|
148
|
-
* @param {
|
|
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:
|
|
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 "
|
|
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 =
|
|
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
|
-
|
|
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
|
|
157
|
+
return is("debug");
|
|
157
158
|
};
|
|
158
159
|
/**
|
|
159
160
|
* @param {string} opt
|
|
160
161
|
* @returns {boolean}
|
|
161
162
|
*/
|
|
162
|
-
export const is = (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,
|
|
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,
|
|
203
|
+
/** @type {Record<string, any>} */
|
|
178
204
|
const qss = {
|
|
179
|
-
|
|
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
|
-
|
|
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}
|
|
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,
|
|
238
|
+
export const fetchP = async (endpointUrl, rqopt, usp, pc, increment = () => { }) => {
|
|
210
239
|
const rqs = [];
|
|
211
240
|
for (let i = 2; i <= pc;) {
|
|
212
|
-
|
|
241
|
+
usp.set("page", (i++) + "");
|
|
213
242
|
increment();
|
|
214
|
-
rqs.push(fetch(`${endpointUrl}?${
|
|
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
|
-
* @
|
|
270
|
+
* @template {unknown} T
|
|
271
|
+
* @param {T} endpoint e.g - "/characters/{character_id}/"
|
|
242
272
|
* @param {number[]} ids
|
|
243
|
-
* @returns {
|
|
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 {
|
|
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
|
-
|
|
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 =
|
|
285
|
-
const rlog =
|
|
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}/",
|
|
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/",
|
|
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: [
|
|
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
|
|
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/",
|
|
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(
|
|
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<
|
|
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
|
|
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<
|
|
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
|
|
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] = /** @
|
|
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 =
|
|
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}/",
|
|
54
|
+
await fn.get("/characters/{character_id}/", ID_CCP_Zoetrope).then(log);
|
|
54
55
|
clog('(portrait)');
|
|
55
|
-
await fn.get("/characters/{character_id}/portrait/",
|
|
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: [
|
|
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
|
-
|
|
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.
|
|
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 <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>
|