@meowpanel/api 1.0.0-beta.2 → 1.0.0-beta.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/esm/src/client.d.ts +6 -25
- package/esm/src/client.d.ts.map +1 -1
- package/esm/src/client.js +7 -9
- package/esm/src/http.d.ts +1 -24
- package/esm/src/http.d.ts.map +1 -1
- package/esm/src/http.js +11 -78
- package/package.json +1 -1
- package/script/src/client.d.ts +6 -25
- package/script/src/client.d.ts.map +1 -1
- package/script/src/client.js +7 -9
- package/script/src/http.d.ts +1 -24
- package/script/src/http.d.ts.map +1 -1
- package/script/src/http.js +11 -78
package/esm/src/client.d.ts
CHANGED
|
@@ -15,19 +15,13 @@ import { ServersNamespace } from './resources/servers/index.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export interface MeowPanelApiOptions {
|
|
17
17
|
/**
|
|
18
|
-
* Base URL of
|
|
18
|
+
* Base URL of MeowPanel instance.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @example `"https://panel.example.com"`
|
|
20
|
+
* @example "https://panel.example.com"
|
|
23
21
|
*/
|
|
24
22
|
url: string | URL;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* Can be a user session token or an API key token.
|
|
29
|
-
*/
|
|
30
|
-
token?: string;
|
|
23
|
+
/** Bearer token used to authenticate every request. */
|
|
24
|
+
token: string;
|
|
31
25
|
/**
|
|
32
26
|
* Custom fetch implementation to use for all HTTP requests.
|
|
33
27
|
*
|
|
@@ -35,24 +29,11 @@ export interface MeowPanelApiOptions {
|
|
|
35
29
|
*/
|
|
36
30
|
fetch?: typeof globalThis.fetch;
|
|
37
31
|
/**
|
|
38
|
-
*
|
|
32
|
+
* Callback invoked for every outgoing request.
|
|
39
33
|
*
|
|
40
34
|
* Useful for integrating API request visibility into your app's debug/info logger.
|
|
41
35
|
*/
|
|
42
36
|
logger?: HttpLogger;
|
|
43
|
-
/**
|
|
44
|
-
* The `credentials` mode for all fetch requests.
|
|
45
|
-
*
|
|
46
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication.
|
|
47
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
48
|
-
*/
|
|
49
|
-
credentials?: RequestCredentials;
|
|
50
|
-
/**
|
|
51
|
-
* Callback invoked when the access token is automatically refreshed.
|
|
52
|
-
*
|
|
53
|
-
* Useful for persisting the new token or updating external state.
|
|
54
|
-
*/
|
|
55
|
-
onTokenRefresh?: (token: string) => void;
|
|
56
37
|
}
|
|
57
38
|
/**
|
|
58
39
|
* The root MeowPanel API client.
|
|
@@ -66,7 +47,7 @@ export interface MeowPanelApiOptions {
|
|
|
66
47
|
*
|
|
67
48
|
* const api = new MeowPanelApi({
|
|
68
49
|
* url: 'https://panel.example.com',
|
|
69
|
-
* token: 'your-
|
|
50
|
+
* token: 'your-api-token',
|
|
70
51
|
* });
|
|
71
52
|
*
|
|
72
53
|
* // Fetch resources
|
package/esm/src/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAClB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAE3D;;;;;;;;OAQG;IACH,SAAgB,IAAI,EAAE,YAAY,CAAC;IAEnC;;;;;;;;;OASG;IACH,SAAgB,OAAO,EAAE,eAAe,CAAC;IAEzC;;;;;;;;OAQG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;;OAWG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;;OAWG;IACH,SAAgB,IAAI,EAAE,aAAa,CAAC;IAEpC;;;;;;;;;OASG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;OAUG;IACH,SAAgB,YAAY,EAAE,qBAAqB,CAAC;IAEpD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;gBAEvB,OAAO,EAAE,mBAAmB;IAyB/C;;;;;;OAMG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOpC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,UAAU,IAAI,aAAa;IAQlC;;;;;OAKG;IACI,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAG/D"}
|
package/esm/src/client.js
CHANGED
|
@@ -21,7 +21,7 @@ import { ServersNamespace } from './resources/servers/index.js';
|
|
|
21
21
|
*
|
|
22
22
|
* const api = new MeowPanelApi({
|
|
23
23
|
* url: 'https://panel.example.com',
|
|
24
|
-
* token: 'your-
|
|
24
|
+
* token: 'your-api-token',
|
|
25
25
|
* });
|
|
26
26
|
*
|
|
27
27
|
* // Fetch resources
|
|
@@ -160,19 +160,14 @@ export class MeowPanelApi {
|
|
|
160
160
|
*/
|
|
161
161
|
servers;
|
|
162
162
|
constructor(options) {
|
|
163
|
-
|
|
163
|
+
if (!options?.url) {
|
|
164
|
+
throw new Error('MeowPanelApi requires a "url" option (e.g. "https://panel.example.com").');
|
|
165
|
+
}
|
|
164
166
|
this.http = new HttpClient({
|
|
165
167
|
url: options.url.toString(),
|
|
166
168
|
token: options.token,
|
|
167
169
|
fetch: options.fetch,
|
|
168
170
|
logger: options.logger,
|
|
169
|
-
credentials: options.credentials,
|
|
170
|
-
onTokenRefresh: (token) => {
|
|
171
|
-
for (const sse of this.sseConnections) {
|
|
172
|
-
sse.updateToken(token);
|
|
173
|
-
}
|
|
174
|
-
userCallback?.(token);
|
|
175
|
-
},
|
|
176
171
|
});
|
|
177
172
|
this.auth = new AuthResource(this.http);
|
|
178
173
|
this.account = new AccountResource(this.http);
|
|
@@ -193,6 +188,9 @@ export class MeowPanelApi {
|
|
|
193
188
|
*/
|
|
194
189
|
setToken(token) {
|
|
195
190
|
this.http.setToken(token);
|
|
191
|
+
for (const sse of this.sseConnections) {
|
|
192
|
+
sse.updateToken(token);
|
|
193
|
+
}
|
|
196
194
|
}
|
|
197
195
|
/**
|
|
198
196
|
* Create a panel-level Server-Sent Events connection to Nexus.
|
package/esm/src/http.d.ts
CHANGED
|
@@ -5,22 +5,11 @@ export interface HttpClientOptions {
|
|
|
5
5
|
/** Base URL of the MeowPanel Nexus API, e.g. `https://panel.example.com`. */
|
|
6
6
|
url: string;
|
|
7
7
|
/** Bearer token used to authenticate every request. */
|
|
8
|
-
token
|
|
8
|
+
token: string;
|
|
9
9
|
/** Custom fetch implementation. Defaults to `globalThis.fetch`. */
|
|
10
10
|
fetch?: typeof globalThis.fetch;
|
|
11
11
|
/** Optional callback invoked for every outgoing request lifecycle event. */
|
|
12
12
|
logger?: HttpLogger;
|
|
13
|
-
/**
|
|
14
|
-
* The `credentials` mode for all fetch requests.
|
|
15
|
-
*
|
|
16
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication
|
|
17
|
-
* (e.g. session cookies set by the auth endpoint).
|
|
18
|
-
*
|
|
19
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
20
|
-
*/
|
|
21
|
-
credentials?: RequestCredentials;
|
|
22
|
-
/** Callback invoked when the access token is refreshed. */
|
|
23
|
-
onTokenRefresh?: (token: string) => void;
|
|
24
13
|
}
|
|
25
14
|
/** Typed lifecycle code emitted by {@link HttpClient}'s optional logger. */
|
|
26
15
|
export type HttpLogCode = 'request.start' | 'request.success' | 'request.error';
|
|
@@ -71,9 +60,6 @@ export declare class HttpClient {
|
|
|
71
60
|
protected token: string;
|
|
72
61
|
protected readonly fetchImpl: typeof globalThis.fetch;
|
|
73
62
|
protected readonly logger?: HttpLogger;
|
|
74
|
-
protected readonly credentials?: RequestCredentials;
|
|
75
|
-
protected readonly onTokenRefresh?: (token: string) => void;
|
|
76
|
-
private refreshPromise;
|
|
77
63
|
constructor(options: HttpClientOptions);
|
|
78
64
|
/** Replace the bearer token for all subsequent requests. */
|
|
79
65
|
setToken(token: string): void;
|
|
@@ -87,18 +73,9 @@ export declare class HttpClient {
|
|
|
87
73
|
protected parseError(response: Response): Promise<ApiError>;
|
|
88
74
|
private log;
|
|
89
75
|
private executeFetch;
|
|
90
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
91
|
-
private getTokenExp;
|
|
92
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
93
|
-
private refreshTokenIfNeeded;
|
|
94
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
95
|
-
private forceRefresh;
|
|
96
76
|
/**
|
|
97
77
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
98
78
|
*
|
|
99
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
100
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
101
|
-
*
|
|
102
79
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
103
80
|
* @param init Standard `RequestInit` options.
|
|
104
81
|
* @param params Query parameters to append.
|
package/esm/src/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IACjC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"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"}
|
package/esm/src/http.js
CHANGED
|
@@ -10,19 +10,20 @@ export class HttpClient {
|
|
|
10
10
|
token;
|
|
11
11
|
fetchImpl;
|
|
12
12
|
logger;
|
|
13
|
-
credentials;
|
|
14
|
-
onTokenRefresh;
|
|
15
|
-
refreshPromise = null;
|
|
16
13
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
17
14
|
return `HttpClient { ${this.baseUrl} }`;
|
|
18
15
|
}
|
|
19
16
|
constructor(options) {
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
try {
|
|
18
|
+
new URL(options.url);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
throw new Error(`Invalid MeowPanel URL: "${options.url}". An absolute URL is required (e.g. "https://panel.example.com").`);
|
|
22
|
+
}
|
|
23
|
+
this.baseUrl = options.url.replace(/\/$/, '') + '/api/v1';
|
|
24
|
+
this.token = options.token;
|
|
22
25
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
23
26
|
this.logger = options.logger;
|
|
24
|
-
this.credentials = options.credentials;
|
|
25
|
-
this.onTokenRefresh = options.onTokenRefresh;
|
|
26
27
|
}
|
|
27
28
|
/** Replace the bearer token for all subsequent requests. */
|
|
28
29
|
setToken(token) {
|
|
@@ -89,7 +90,7 @@ export class HttpClient {
|
|
|
89
90
|
params,
|
|
90
91
|
});
|
|
91
92
|
try {
|
|
92
|
-
const response = await this.fetchImpl(url,
|
|
93
|
+
const response = await this.fetchImpl(url, init);
|
|
93
94
|
const durationMs = Date.now() - startedAt;
|
|
94
95
|
if (!response.ok) {
|
|
95
96
|
const error = await this.parseError(response);
|
|
@@ -134,86 +135,18 @@ export class HttpClient {
|
|
|
134
135
|
throw error;
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
138
|
-
getTokenExp() {
|
|
139
|
-
const parts = this.token.split('.');
|
|
140
|
-
if (parts.length !== 3)
|
|
141
|
-
return 0; // Not a JWT token
|
|
142
|
-
try {
|
|
143
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
144
|
-
return typeof payload.exp === 'number' ? payload.exp : 0;
|
|
145
|
-
}
|
|
146
|
-
catch {
|
|
147
|
-
return 0;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
151
|
-
async refreshTokenIfNeeded() {
|
|
152
|
-
const exp = this.getTokenExp();
|
|
153
|
-
if (exp === 0)
|
|
154
|
-
return;
|
|
155
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
156
|
-
if (exp - nowSec > 60)
|
|
157
|
-
return;
|
|
158
|
-
await this.forceRefresh();
|
|
159
|
-
}
|
|
160
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
161
|
-
async forceRefresh() {
|
|
162
|
-
if (this.refreshPromise) {
|
|
163
|
-
await this.refreshPromise;
|
|
164
|
-
return !!this.token;
|
|
165
|
-
}
|
|
166
|
-
this.refreshPromise = (async () => {
|
|
167
|
-
try {
|
|
168
|
-
const res = await this.fetchImpl(this.baseUrl + '/auth/refresh', {
|
|
169
|
-
method: 'POST',
|
|
170
|
-
headers: {
|
|
171
|
-
'Content-Type': 'application/json',
|
|
172
|
-
'Accept': 'application/json',
|
|
173
|
-
},
|
|
174
|
-
credentials: this.credentials,
|
|
175
|
-
});
|
|
176
|
-
if (res.ok) {
|
|
177
|
-
const data = await res.json();
|
|
178
|
-
this.token = data.access_token;
|
|
179
|
-
this.onTokenRefresh?.(data.access_token);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
finally {
|
|
183
|
-
this.refreshPromise = null;
|
|
184
|
-
}
|
|
185
|
-
})();
|
|
186
|
-
await this.refreshPromise;
|
|
187
|
-
return !!this.token;
|
|
188
|
-
}
|
|
189
138
|
/**
|
|
190
139
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
191
140
|
*
|
|
192
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
193
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
194
|
-
*
|
|
195
141
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
196
142
|
* @param init Standard `RequestInit` options.
|
|
197
143
|
* @param params Query parameters to append.
|
|
198
144
|
*/
|
|
199
145
|
async fetch(path, init = {}, params) {
|
|
200
|
-
await this.
|
|
201
|
-
const buildInit = () => ({
|
|
146
|
+
return await this.executeFetch(path, {
|
|
202
147
|
...init,
|
|
203
148
|
headers: { ...this.defaultHeaders, ...init.headers },
|
|
204
|
-
});
|
|
205
|
-
try {
|
|
206
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
if (error instanceof ApiError && error.status === 401 && path !== '/auth/refresh') {
|
|
210
|
-
const refreshed = await this.forceRefresh();
|
|
211
|
-
if (refreshed) {
|
|
212
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
throw error;
|
|
216
|
-
}
|
|
149
|
+
}, params);
|
|
217
150
|
}
|
|
218
151
|
/**
|
|
219
152
|
* GET and deserialize a JSON response body.
|
package/package.json
CHANGED
package/script/src/client.d.ts
CHANGED
|
@@ -15,19 +15,13 @@ import { ServersNamespace } from './resources/servers/index.js';
|
|
|
15
15
|
*/
|
|
16
16
|
export interface MeowPanelApiOptions {
|
|
17
17
|
/**
|
|
18
|
-
* Base URL of
|
|
18
|
+
* Base URL of MeowPanel instance.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @example `"https://panel.example.com"`
|
|
20
|
+
* @example "https://panel.example.com"
|
|
23
21
|
*/
|
|
24
22
|
url: string | URL;
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
*
|
|
28
|
-
* Can be a user session token or an API key token.
|
|
29
|
-
*/
|
|
30
|
-
token?: string;
|
|
23
|
+
/** Bearer token used to authenticate every request. */
|
|
24
|
+
token: string;
|
|
31
25
|
/**
|
|
32
26
|
* Custom fetch implementation to use for all HTTP requests.
|
|
33
27
|
*
|
|
@@ -35,24 +29,11 @@ export interface MeowPanelApiOptions {
|
|
|
35
29
|
*/
|
|
36
30
|
fetch?: typeof globalThis.fetch;
|
|
37
31
|
/**
|
|
38
|
-
*
|
|
32
|
+
* Callback invoked for every outgoing request.
|
|
39
33
|
*
|
|
40
34
|
* Useful for integrating API request visibility into your app's debug/info logger.
|
|
41
35
|
*/
|
|
42
36
|
logger?: HttpLogger;
|
|
43
|
-
/**
|
|
44
|
-
* The `credentials` mode for all fetch requests.
|
|
45
|
-
*
|
|
46
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication.
|
|
47
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
48
|
-
*/
|
|
49
|
-
credentials?: RequestCredentials;
|
|
50
|
-
/**
|
|
51
|
-
* Callback invoked when the access token is automatically refreshed.
|
|
52
|
-
*
|
|
53
|
-
* Useful for persisting the new token or updating external state.
|
|
54
|
-
*/
|
|
55
|
-
onTokenRefresh?: (token: string) => void;
|
|
56
37
|
}
|
|
57
38
|
/**
|
|
58
39
|
* The root MeowPanel API client.
|
|
@@ -66,7 +47,7 @@ export interface MeowPanelApiOptions {
|
|
|
66
47
|
*
|
|
67
48
|
* const api = new MeowPanelApi({
|
|
68
49
|
* url: 'https://panel.example.com',
|
|
69
|
-
* token: 'your-
|
|
50
|
+
* token: 'your-api-token',
|
|
70
51
|
* });
|
|
71
52
|
*
|
|
72
53
|
* // Fetch resources
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAClB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,YAAY;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAE3D;;;;;;;;OAQG;IACH,SAAgB,IAAI,EAAE,YAAY,CAAC;IAEnC;;;;;;;;;OASG;IACH,SAAgB,OAAO,EAAE,eAAe,CAAC;IAEzC;;;;;;;;OAQG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;;OAWG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;;OAWG;IACH,SAAgB,IAAI,EAAE,aAAa,CAAC;IAEpC;;;;;;;;;OASG;IACH,SAAgB,KAAK,EAAE,cAAc,CAAC;IAEtC;;;;;;;;;;OAUG;IACH,SAAgB,YAAY,EAAE,qBAAqB,CAAC;IAEpD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAgB,OAAO,EAAE,gBAAgB,CAAC;gBAEvB,OAAO,EAAE,mBAAmB;IAyB/C;;;;;;OAMG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOpC;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,UAAU,IAAI,aAAa;IAQlC;;;;;OAKG;IACI,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAG/D"}
|
package/script/src/client.js
CHANGED
|
@@ -24,7 +24,7 @@ const index_js_1 = require("./resources/servers/index.js");
|
|
|
24
24
|
*
|
|
25
25
|
* const api = new MeowPanelApi({
|
|
26
26
|
* url: 'https://panel.example.com',
|
|
27
|
-
* token: 'your-
|
|
27
|
+
* token: 'your-api-token',
|
|
28
28
|
* });
|
|
29
29
|
*
|
|
30
30
|
* // Fetch resources
|
|
@@ -163,19 +163,14 @@ class MeowPanelApi {
|
|
|
163
163
|
*/
|
|
164
164
|
servers;
|
|
165
165
|
constructor(options) {
|
|
166
|
-
|
|
166
|
+
if (!options?.url) {
|
|
167
|
+
throw new Error('MeowPanelApi requires a "url" option (e.g. "https://panel.example.com").');
|
|
168
|
+
}
|
|
167
169
|
this.http = new http_js_1.HttpClient({
|
|
168
170
|
url: options.url.toString(),
|
|
169
171
|
token: options.token,
|
|
170
172
|
fetch: options.fetch,
|
|
171
173
|
logger: options.logger,
|
|
172
|
-
credentials: options.credentials,
|
|
173
|
-
onTokenRefresh: (token) => {
|
|
174
|
-
for (const sse of this.sseConnections) {
|
|
175
|
-
sse.updateToken(token);
|
|
176
|
-
}
|
|
177
|
-
userCallback?.(token);
|
|
178
|
-
},
|
|
179
174
|
});
|
|
180
175
|
this.auth = new auth_js_1.AuthResource(this.http);
|
|
181
176
|
this.account = new account_js_1.AccountResource(this.http);
|
|
@@ -196,6 +191,9 @@ class MeowPanelApi {
|
|
|
196
191
|
*/
|
|
197
192
|
setToken(token) {
|
|
198
193
|
this.http.setToken(token);
|
|
194
|
+
for (const sse of this.sseConnections) {
|
|
195
|
+
sse.updateToken(token);
|
|
196
|
+
}
|
|
199
197
|
}
|
|
200
198
|
/**
|
|
201
199
|
* Create a panel-level Server-Sent Events connection to Nexus.
|
package/script/src/http.d.ts
CHANGED
|
@@ -5,22 +5,11 @@ export interface HttpClientOptions {
|
|
|
5
5
|
/** Base URL of the MeowPanel Nexus API, e.g. `https://panel.example.com`. */
|
|
6
6
|
url: string;
|
|
7
7
|
/** Bearer token used to authenticate every request. */
|
|
8
|
-
token
|
|
8
|
+
token: string;
|
|
9
9
|
/** Custom fetch implementation. Defaults to `globalThis.fetch`. */
|
|
10
10
|
fetch?: typeof globalThis.fetch;
|
|
11
11
|
/** Optional callback invoked for every outgoing request lifecycle event. */
|
|
12
12
|
logger?: HttpLogger;
|
|
13
|
-
/**
|
|
14
|
-
* The `credentials` mode for all fetch requests.
|
|
15
|
-
*
|
|
16
|
-
* Set to `'include'` for browser apps that rely on cookie-based authentication
|
|
17
|
-
* (e.g. session cookies set by the auth endpoint).
|
|
18
|
-
*
|
|
19
|
-
* Defaults to `undefined` (browser default is `'same-origin'`).
|
|
20
|
-
*/
|
|
21
|
-
credentials?: RequestCredentials;
|
|
22
|
-
/** Callback invoked when the access token is refreshed. */
|
|
23
|
-
onTokenRefresh?: (token: string) => void;
|
|
24
13
|
}
|
|
25
14
|
/** Typed lifecycle code emitted by {@link HttpClient}'s optional logger. */
|
|
26
15
|
export type HttpLogCode = 'request.start' | 'request.success' | 'request.error';
|
|
@@ -71,9 +60,6 @@ export declare class HttpClient {
|
|
|
71
60
|
protected token: string;
|
|
72
61
|
protected readonly fetchImpl: typeof globalThis.fetch;
|
|
73
62
|
protected readonly logger?: HttpLogger;
|
|
74
|
-
protected readonly credentials?: RequestCredentials;
|
|
75
|
-
protected readonly onTokenRefresh?: (token: string) => void;
|
|
76
|
-
private refreshPromise;
|
|
77
63
|
constructor(options: HttpClientOptions);
|
|
78
64
|
/** Replace the bearer token for all subsequent requests. */
|
|
79
65
|
setToken(token: string): void;
|
|
@@ -87,18 +73,9 @@ export declare class HttpClient {
|
|
|
87
73
|
protected parseError(response: Response): Promise<ApiError>;
|
|
88
74
|
private log;
|
|
89
75
|
private executeFetch;
|
|
90
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
91
|
-
private getTokenExp;
|
|
92
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
93
|
-
private refreshTokenIfNeeded;
|
|
94
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
95
|
-
private forceRefresh;
|
|
96
76
|
/**
|
|
97
77
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
98
78
|
*
|
|
99
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
100
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
101
|
-
*
|
|
102
79
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
103
80
|
* @param init Standard `RequestInit` options.
|
|
104
81
|
* @param params Query parameters to append.
|
package/script/src/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IACjC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEzD,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IACjC,6EAA6E;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,UAAU,CAAC;CACpB;AAED,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,iBAAiB,GAAG,eAAe,CAAC;AAEhF,wDAAwD;AACxD,UAAU,gBAAgB;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;CAC/D;AAED,oDAAoD;AACpD,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IACjE,IAAI,EAAE,eAAe,CAAC;CACtB;AAED,uDAAuD;AACvD,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IACnE,IAAI,EAAE,iBAAiB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IACjE,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;CACb;AAED,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GACrB,wBAAwB,GACxB,0BAA0B,GAC1B,wBAAwB,CAAC;AAE5B,8DAA8D;AAC9D,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;AAEvD,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC9B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;IAC/D,8CAA8C;IAC9C,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;GAKG;AACH,qBAAa,UAAU;IACtB,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IACtD,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;gBAMpB,OAAO,EAAE,iBAAiB;IAe7C,4DAA4D;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC,uCAAuC;IAChC,QAAQ,IAAI,MAAM;IAIzB,kEAAkE;IAC3D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GAAG,MAAM;IAUrG,mDAAmD;IACnD,SAAS,KAAK,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOrD;IAED,8DAA8D;cAC9C,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAYjE,OAAO,CAAC,GAAG;YAQG,YAAY;IAiE1B;;;;;;OAMG;IACU,KAAK,CACjB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GAC5D,OAAO,CAAC,QAAQ,CAAC;IAOpB;;;;;OAKG;IACU,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAKxE;;;;;;OAMG;IACU,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAUhG;;;;;;OAMG;IACU,KAAK,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAUjG;;;;;OAKG;IACU,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3E;;;;;;;OAOG;IACU,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAapH;;;;;;;OAOG;IACU,IAAI,CAAC,CAAC,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GACjE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;CAwBxB"}
|
package/script/src/http.js
CHANGED
|
@@ -13,19 +13,20 @@ class HttpClient {
|
|
|
13
13
|
token;
|
|
14
14
|
fetchImpl;
|
|
15
15
|
logger;
|
|
16
|
-
credentials;
|
|
17
|
-
onTokenRefresh;
|
|
18
|
-
refreshPromise = null;
|
|
19
16
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
20
17
|
return `HttpClient { ${this.baseUrl} }`;
|
|
21
18
|
}
|
|
22
19
|
constructor(options) {
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
try {
|
|
21
|
+
new URL(options.url);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`Invalid MeowPanel URL: "${options.url}". An absolute URL is required (e.g. "https://panel.example.com").`);
|
|
25
|
+
}
|
|
26
|
+
this.baseUrl = options.url.replace(/\/$/, '') + '/api/v1';
|
|
27
|
+
this.token = options.token;
|
|
25
28
|
this.fetchImpl = options.fetch ?? globalThis.fetch;
|
|
26
29
|
this.logger = options.logger;
|
|
27
|
-
this.credentials = options.credentials;
|
|
28
|
-
this.onTokenRefresh = options.onTokenRefresh;
|
|
29
30
|
}
|
|
30
31
|
/** Replace the bearer token for all subsequent requests. */
|
|
31
32
|
setToken(token) {
|
|
@@ -92,7 +93,7 @@ class HttpClient {
|
|
|
92
93
|
params,
|
|
93
94
|
});
|
|
94
95
|
try {
|
|
95
|
-
const response = await this.fetchImpl(url,
|
|
96
|
+
const response = await this.fetchImpl(url, init);
|
|
96
97
|
const durationMs = Date.now() - startedAt;
|
|
97
98
|
if (!response.ok) {
|
|
98
99
|
const error = await this.parseError(response);
|
|
@@ -137,86 +138,18 @@ class HttpClient {
|
|
|
137
138
|
throw error;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
|
-
/** Extract the `exp` claim from a JWT without verification. Returns 0 for non-JWT tokens. */
|
|
141
|
-
getTokenExp() {
|
|
142
|
-
const parts = this.token.split('.');
|
|
143
|
-
if (parts.length !== 3)
|
|
144
|
-
return 0; // Not a JWT token
|
|
145
|
-
try {
|
|
146
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
147
|
-
return typeof payload.exp === 'number' ? payload.exp : 0;
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return 0;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/** Refresh the access token if it's a JWT and about to expire. */
|
|
154
|
-
async refreshTokenIfNeeded() {
|
|
155
|
-
const exp = this.getTokenExp();
|
|
156
|
-
if (exp === 0)
|
|
157
|
-
return;
|
|
158
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
159
|
-
if (exp - nowSec > 60)
|
|
160
|
-
return;
|
|
161
|
-
await this.forceRefresh();
|
|
162
|
-
}
|
|
163
|
-
/** Force a token refresh regardless of expiry. Only works if credentials are set (cookie auth). */
|
|
164
|
-
async forceRefresh() {
|
|
165
|
-
if (this.refreshPromise) {
|
|
166
|
-
await this.refreshPromise;
|
|
167
|
-
return !!this.token;
|
|
168
|
-
}
|
|
169
|
-
this.refreshPromise = (async () => {
|
|
170
|
-
try {
|
|
171
|
-
const res = await this.fetchImpl(this.baseUrl + '/auth/refresh', {
|
|
172
|
-
method: 'POST',
|
|
173
|
-
headers: {
|
|
174
|
-
'Content-Type': 'application/json',
|
|
175
|
-
'Accept': 'application/json',
|
|
176
|
-
},
|
|
177
|
-
credentials: this.credentials,
|
|
178
|
-
});
|
|
179
|
-
if (res.ok) {
|
|
180
|
-
const data = await res.json();
|
|
181
|
-
this.token = data.access_token;
|
|
182
|
-
this.onTokenRefresh?.(data.access_token);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
finally {
|
|
186
|
-
this.refreshPromise = null;
|
|
187
|
-
}
|
|
188
|
-
})();
|
|
189
|
-
await this.refreshPromise;
|
|
190
|
-
return !!this.token;
|
|
191
|
-
}
|
|
192
141
|
/**
|
|
193
142
|
* Perform a raw `fetch` and throw {@link ApiError} on non-2xx responses.
|
|
194
143
|
*
|
|
195
|
-
* Proactively refreshes JWT tokens that are about to expire and retries
|
|
196
|
-
* once on `401 Unauthorized` after forcing a token refresh.
|
|
197
|
-
*
|
|
198
144
|
* @param path Path relative to the API base URL, e.g. `/servers`.
|
|
199
145
|
* @param init Standard `RequestInit` options.
|
|
200
146
|
* @param params Query parameters to append.
|
|
201
147
|
*/
|
|
202
148
|
async fetch(path, init = {}, params) {
|
|
203
|
-
await this.
|
|
204
|
-
const buildInit = () => ({
|
|
149
|
+
return await this.executeFetch(path, {
|
|
205
150
|
...init,
|
|
206
151
|
headers: { ...this.defaultHeaders, ...init.headers },
|
|
207
|
-
});
|
|
208
|
-
try {
|
|
209
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
if (error instanceof errors_js_1.ApiError && error.status === 401 && path !== '/auth/refresh') {
|
|
213
|
-
const refreshed = await this.forceRefresh();
|
|
214
|
-
if (refreshed) {
|
|
215
|
-
return await this.executeFetch(path, buildInit(), params);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
152
|
+
}, params);
|
|
220
153
|
}
|
|
221
154
|
/**
|
|
222
155
|
* GET and deserialize a JSON response body.
|