iobroker.beszel 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/build/lib/beszel-client.js +197 -194
- package/build/lib/beszel-client.js.map +7 -0
- package/build/lib/state-manager.js +910 -714
- package/build/lib/state-manager.js.map +7 -0
- package/build/lib/types.js +16 -1
- package/build/lib/types.js.map +7 -0
- package/build/main.js +211 -179
- package/build/main.js.map +7 -0
- package/io-package.json +27 -27
- package/package.json +11 -8
- package/build/lib/beszel-client.d.ts +0 -44
- package/build/lib/beszel-client.d.ts.map +0 -1
- package/build/lib/state-manager.d.ts +0 -57
- package/build/lib/state-manager.d.ts.map +0 -1
- package/build/lib/types.d.ts +0 -223
- package/build/lib/types.d.ts.map +0 -1
- package/build/main.d.ts +0 -2
- package/build/main.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -122,6 +122,12 @@ beszel.0.
|
|
|
122
122
|
|
|
123
123
|
## Changelog
|
|
124
124
|
|
|
125
|
+
### 0.2.2 (2026-04-03)
|
|
126
|
+
- Modernize dev tooling (esbuild, TypeScript 5.9 pin, testing-action-check v2)
|
|
127
|
+
|
|
128
|
+
### 0.2.1 (2026-03-28)
|
|
129
|
+
- Error deduplication, auth backoff, protect against empty system list deletion
|
|
130
|
+
|
|
125
131
|
### 0.2.0 (2026-03-28)
|
|
126
132
|
- Adapter timer methods, synchronous onUnload, merge About tab, Windows/macOS CI, full MIT license
|
|
127
133
|
|
|
@@ -137,12 +143,6 @@ beszel.0.
|
|
|
137
143
|
### 0.1.6 (2026-03-18)
|
|
138
144
|
- Code cleanup: remove dead code, fix duplicate container filter, extract load avg helper
|
|
139
145
|
|
|
140
|
-
### 0.1.5 (2026-03-17)
|
|
141
|
-
- Migrate to @alcalzone/release-script, enable npm Trusted Publishing
|
|
142
|
-
|
|
143
|
-
### 0.1.4 (2026-03-17)
|
|
144
|
-
- Fix all repochecker issues; rename repo to ioBroker.beszel; add responsive grid sizes
|
|
145
|
-
|
|
146
146
|
Older changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
147
147
|
|
|
148
148
|
---
|
|
@@ -1,204 +1,207 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var beszel_client_exports = {};
|
|
30
|
+
__export(beszel_client_exports, {
|
|
31
|
+
BeszelClient: () => BeszelClient
|
|
17
32
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.BeszelClient = void 0;
|
|
37
|
-
const http = __importStar(require("http"));
|
|
38
|
-
const https = __importStar(require("https"));
|
|
39
|
-
const url_1 = require("url");
|
|
40
|
-
const TOKEN_REFRESH_MS = 23 * 60 * 60 * 1000; // 23 hours
|
|
41
|
-
/**
|
|
42
|
-
* HTTP client for the Beszel PocketBase REST API.
|
|
43
|
-
* Uses only Node.js built-in http/https — no extra dependencies.
|
|
44
|
-
*/
|
|
33
|
+
module.exports = __toCommonJS(beszel_client_exports);
|
|
34
|
+
var http = __toESM(require("http"));
|
|
35
|
+
var https = __toESM(require("https"));
|
|
36
|
+
var import_url = require("url");
|
|
37
|
+
const TOKEN_REFRESH_MS = 23 * 60 * 60 * 1e3;
|
|
45
38
|
class BeszelClient {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
message: err instanceof Error ? err.message : String(err),
|
|
81
|
-
};
|
|
82
|
-
}
|
|
39
|
+
baseUrl;
|
|
40
|
+
username;
|
|
41
|
+
password;
|
|
42
|
+
token = null;
|
|
43
|
+
tokenTime = 0;
|
|
44
|
+
/**
|
|
45
|
+
* @param url Beszel Hub base URL, e.g. http://192.168.1.100:8090
|
|
46
|
+
* @param username Login username
|
|
47
|
+
* @param password Login password
|
|
48
|
+
*/
|
|
49
|
+
constructor(url, username, password) {
|
|
50
|
+
this.baseUrl = url.replace(/\/+$/, "");
|
|
51
|
+
this.username = username;
|
|
52
|
+
this.password = password;
|
|
53
|
+
}
|
|
54
|
+
/** Force token re-authentication on the next request */
|
|
55
|
+
invalidateToken() {
|
|
56
|
+
this.token = null;
|
|
57
|
+
this.tokenTime = 0;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Test the connection to Beszel.
|
|
61
|
+
* Returns { success: true } or { success: false, message: reason }.
|
|
62
|
+
*/
|
|
63
|
+
async checkConnection() {
|
|
64
|
+
try {
|
|
65
|
+
this.invalidateToken();
|
|
66
|
+
await this.authenticate();
|
|
67
|
+
return { success: true, message: "Connected successfully" };
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
message: err instanceof Error ? err.message : String(err)
|
|
72
|
+
};
|
|
83
73
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
}
|
|
75
|
+
/** Fetch all systems */
|
|
76
|
+
async getSystems() {
|
|
77
|
+
await this.ensureToken();
|
|
78
|
+
const data = await this.fetchJson(
|
|
79
|
+
"/api/collections/systems/records?perPage=200&sort=name"
|
|
80
|
+
);
|
|
81
|
+
return data.items;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Fetch the latest 1m stats per system.
|
|
85
|
+
* Returns a Map<systemId, SystemStats>.
|
|
86
|
+
*
|
|
87
|
+
* @param systemIds List of system IDs to fetch stats for
|
|
88
|
+
*/
|
|
89
|
+
async getLatestStats(systemIds) {
|
|
90
|
+
if (systemIds.length === 0) {
|
|
91
|
+
return /* @__PURE__ */ new Map();
|
|
89
92
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
await this.ensureToken();
|
|
101
|
-
const data = await this.fetchJson("/api/collections/system_stats/records?sort=-updated&perPage=200&filter=type%3D'1m'");
|
|
102
|
-
// Deduplicate: keep the newest record per system
|
|
103
|
-
const result = new Map();
|
|
104
|
-
for (const record of data.items) {
|
|
105
|
-
if (!result.has(record.system)) {
|
|
106
|
-
result.set(record.system, record.stats);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return result;
|
|
93
|
+
await this.ensureToken();
|
|
94
|
+
const data = await this.fetchJson(
|
|
95
|
+
"/api/collections/system_stats/records?sort=-updated&perPage=200&filter=type%3D'1m'"
|
|
96
|
+
);
|
|
97
|
+
const result = /* @__PURE__ */ new Map();
|
|
98
|
+
for (const record of data.items) {
|
|
99
|
+
if (!result.has(record.system)) {
|
|
100
|
+
result.set(record.system, record.stats);
|
|
101
|
+
}
|
|
110
102
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
/** Fetch all containers */
|
|
106
|
+
async getContainers() {
|
|
107
|
+
await this.ensureToken();
|
|
108
|
+
const data = await this.fetchJson(
|
|
109
|
+
"/api/collections/containers/records?perPage=500&sort=system%2Cname"
|
|
110
|
+
);
|
|
111
|
+
return data.items;
|
|
112
|
+
}
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
// Private helpers
|
|
115
|
+
// -------------------------------------------------------------------------
|
|
116
|
+
async ensureToken() {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
if (this.token && now - this.tokenTime < TOKEN_REFRESH_MS) {
|
|
119
|
+
return;
|
|
116
120
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
await this.authenticate();
|
|
122
|
+
}
|
|
123
|
+
async authenticate() {
|
|
124
|
+
const body = JSON.stringify({
|
|
125
|
+
identity: this.username,
|
|
126
|
+
password: this.password
|
|
127
|
+
});
|
|
128
|
+
const data = await this.request(
|
|
129
|
+
"POST",
|
|
130
|
+
"/api/collections/users/auth-with-password",
|
|
131
|
+
body,
|
|
132
|
+
null
|
|
133
|
+
// no auth token yet
|
|
134
|
+
);
|
|
135
|
+
this.token = data.token;
|
|
136
|
+
this.tokenTime = Date.now();
|
|
137
|
+
}
|
|
138
|
+
async fetchJson(path) {
|
|
139
|
+
return this.request("GET", path, null, this.token);
|
|
140
|
+
}
|
|
141
|
+
request(method, path, body, token) {
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
let parsedUrl;
|
|
144
|
+
try {
|
|
145
|
+
parsedUrl = new import_url.URL(this.baseUrl + path);
|
|
146
|
+
} catch {
|
|
147
|
+
reject(new Error(`Invalid URL: ${this.baseUrl + path}`));
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const isHttps = parsedUrl.protocol === "https:";
|
|
151
|
+
const transport = isHttps ? https : http;
|
|
152
|
+
const headers = {
|
|
153
|
+
"Content-Type": "application/json",
|
|
154
|
+
Accept: "application/json"
|
|
155
|
+
};
|
|
156
|
+
if (token) {
|
|
157
|
+
headers.Authorization = token;
|
|
158
|
+
}
|
|
159
|
+
if (body !== null) {
|
|
160
|
+
headers["Content-Length"] = Buffer.byteLength(body).toString();
|
|
161
|
+
}
|
|
162
|
+
const options = {
|
|
163
|
+
hostname: parsedUrl.hostname,
|
|
164
|
+
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
165
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
166
|
+
method,
|
|
167
|
+
headers,
|
|
168
|
+
timeout: 15e3
|
|
169
|
+
};
|
|
170
|
+
const req = transport.request(options, (res) => {
|
|
171
|
+
const chunks = [];
|
|
172
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
173
|
+
res.on("end", () => {
|
|
174
|
+
var _a;
|
|
175
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
176
|
+
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
|
|
177
|
+
const err = new Error(
|
|
178
|
+
`HTTP ${(_a = res.statusCode) != null ? _a : "?"}: ${raw.slice(0, 200)}`
|
|
179
|
+
);
|
|
180
|
+
err.code = res.statusCode === 401 ? "UNAUTHORIZED" : "HTTP_ERROR";
|
|
181
|
+
reject(err);
|
|
123
182
|
return;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
password: this.password,
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
resolve(JSON.parse(raw));
|
|
186
|
+
} catch {
|
|
187
|
+
reject(new Error(`Invalid JSON response from ${path}`));
|
|
188
|
+
}
|
|
131
189
|
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
reject(new Error(`Invalid URL: ${this.baseUrl + path}`));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
const isHttps = parsedUrl.protocol === "https:";
|
|
150
|
-
const transport = isHttps ? https : http;
|
|
151
|
-
const headers = {
|
|
152
|
-
"Content-Type": "application/json",
|
|
153
|
-
Accept: "application/json",
|
|
154
|
-
};
|
|
155
|
-
if (token) {
|
|
156
|
-
headers.Authorization = token;
|
|
157
|
-
}
|
|
158
|
-
if (body !== null) {
|
|
159
|
-
headers["Content-Length"] = Buffer.byteLength(body).toString();
|
|
160
|
-
}
|
|
161
|
-
const options = {
|
|
162
|
-
hostname: parsedUrl.hostname,
|
|
163
|
-
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
164
|
-
path: parsedUrl.pathname + parsedUrl.search,
|
|
165
|
-
method,
|
|
166
|
-
headers,
|
|
167
|
-
timeout: 15000,
|
|
168
|
-
};
|
|
169
|
-
const req = transport.request(options, (res) => {
|
|
170
|
-
const chunks = [];
|
|
171
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
172
|
-
res.on("end", () => {
|
|
173
|
-
const raw = Buffer.concat(chunks).toString("utf8");
|
|
174
|
-
if (!res.statusCode ||
|
|
175
|
-
res.statusCode < 200 ||
|
|
176
|
-
res.statusCode >= 300) {
|
|
177
|
-
// Propagate 401 specifically so caller can re-auth
|
|
178
|
-
const err = new Error(`HTTP ${res.statusCode ?? "?"}: ${raw.slice(0, 200)}`);
|
|
179
|
-
err.code =
|
|
180
|
-
res.statusCode === 401 ? "UNAUTHORIZED" : "HTTP_ERROR";
|
|
181
|
-
reject(err);
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
resolve(JSON.parse(raw));
|
|
186
|
-
}
|
|
187
|
-
catch {
|
|
188
|
-
reject(new Error(`Invalid JSON response from ${path}`));
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
req.on("timeout", () => {
|
|
193
|
-
req.destroy();
|
|
194
|
-
reject(new Error(`Request to ${path} timed out`));
|
|
195
|
-
});
|
|
196
|
-
req.on("error", (err) => reject(err));
|
|
197
|
-
if (body !== null) {
|
|
198
|
-
req.write(body);
|
|
199
|
-
}
|
|
200
|
-
req.end();
|
|
201
|
-
});
|
|
202
|
-
}
|
|
190
|
+
});
|
|
191
|
+
req.on("timeout", () => {
|
|
192
|
+
req.destroy();
|
|
193
|
+
reject(new Error(`Request to ${path} timed out`));
|
|
194
|
+
});
|
|
195
|
+
req.on("error", (err) => reject(err));
|
|
196
|
+
if (body !== null) {
|
|
197
|
+
req.write(body);
|
|
198
|
+
}
|
|
199
|
+
req.end();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
203
202
|
}
|
|
204
|
-
|
|
203
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
204
|
+
0 && (module.exports = {
|
|
205
|
+
BeszelClient
|
|
206
|
+
});
|
|
207
|
+
//# sourceMappingURL=beszel-client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/beszel-client.ts"],
|
|
4
|
+
"sourcesContent": ["import * as http from \"http\";\nimport * as https from \"https\";\nimport { URL } from \"url\";\nimport type {\n AuthResponse,\n BeszelContainer,\n BeszelSystem,\n BeszelSystemStats,\n PocketBaseList,\n SystemStats,\n} from \"./types.js\";\n\nconst TOKEN_REFRESH_MS = 23 * 60 * 60 * 1000; // 23 hours\n\n/**\n * HTTP client for the Beszel PocketBase REST API.\n * Uses only Node.js built-in http/https \u2014 no extra dependencies.\n */\nexport class BeszelClient {\n private readonly baseUrl: string;\n private readonly username: string;\n private readonly password: string;\n\n private token: string | null = null;\n private tokenTime = 0;\n\n /**\n * @param url Beszel Hub base URL, e.g. http://192.168.1.100:8090\n * @param username Login username\n * @param password Login password\n */\n constructor(url: string, username: string, password: string) {\n // Strip trailing slash\n this.baseUrl = url.replace(/\\/+$/, \"\");\n this.username = username;\n this.password = password;\n }\n\n /** Force token re-authentication on the next request */\n public invalidateToken(): void {\n this.token = null;\n this.tokenTime = 0;\n }\n\n /**\n * Test the connection to Beszel.\n * Returns { success: true } or { success: false, message: reason }.\n */\n public async checkConnection(): Promise<{\n success: boolean;\n message: string;\n }> {\n try {\n this.invalidateToken();\n await this.authenticate();\n return { success: true, message: \"Connected successfully\" };\n } catch (err) {\n return {\n success: false,\n message: err instanceof Error ? err.message : String(err),\n };\n }\n }\n\n /** Fetch all systems */\n public async getSystems(): Promise<BeszelSystem[]> {\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelSystem>>(\n \"/api/collections/systems/records?perPage=200&sort=name\",\n );\n return data.items;\n }\n\n /**\n * Fetch the latest 1m stats per system.\n * Returns a Map<systemId, SystemStats>.\n *\n * @param systemIds List of system IDs to fetch stats for\n */\n public async getLatestStats(\n systemIds: string[],\n ): Promise<Map<string, SystemStats>> {\n if (systemIds.length === 0) {\n return new Map();\n }\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelSystemStats>>(\n \"/api/collections/system_stats/records?sort=-updated&perPage=200&filter=type%3D'1m'\",\n );\n\n // Deduplicate: keep the newest record per system\n const result = new Map<string, SystemStats>();\n for (const record of data.items) {\n if (!result.has(record.system)) {\n result.set(record.system, record.stats);\n }\n }\n return result;\n }\n\n /** Fetch all containers */\n public async getContainers(): Promise<BeszelContainer[]> {\n await this.ensureToken();\n const data = await this.fetchJson<PocketBaseList<BeszelContainer>>(\n \"/api/collections/containers/records?perPage=500&sort=system%2Cname\",\n );\n return data.items;\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private async ensureToken(): Promise<void> {\n const now = Date.now();\n if (this.token && now - this.tokenTime < TOKEN_REFRESH_MS) {\n return;\n }\n await this.authenticate();\n }\n\n private async authenticate(): Promise<void> {\n const body = JSON.stringify({\n identity: this.username,\n password: this.password,\n });\n\n const data = await this.request<AuthResponse>(\n \"POST\",\n \"/api/collections/users/auth-with-password\",\n body,\n null, // no auth token yet\n );\n\n this.token = data.token;\n this.tokenTime = Date.now();\n }\n\n private async fetchJson<T>(path: string): Promise<T> {\n return this.request<T>(\"GET\", path, null, this.token);\n }\n\n private request<T>(\n method: string,\n path: string,\n body: string | null,\n token: string | null,\n ): Promise<T> {\n return new Promise((resolve, reject) => {\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(this.baseUrl + path);\n } catch {\n reject(new Error(`Invalid URL: ${this.baseUrl + path}`));\n return;\n }\n\n const isHttps = parsedUrl.protocol === \"https:\";\n const transport = isHttps ? https : http;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n };\n if (token) {\n headers.Authorization = token;\n }\n if (body !== null) {\n headers[\"Content-Length\"] = Buffer.byteLength(body).toString();\n }\n\n const options: http.RequestOptions = {\n hostname: parsedUrl.hostname,\n port: parsedUrl.port || (isHttps ? 443 : 80),\n path: parsedUrl.pathname + parsedUrl.search,\n method,\n headers,\n timeout: 15000,\n };\n\n const req = transport.request(options, (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const raw = Buffer.concat(chunks).toString(\"utf8\");\n if (\n !res.statusCode ||\n res.statusCode < 200 ||\n res.statusCode >= 300\n ) {\n // Propagate 401 specifically so caller can re-auth\n const err = new Error(\n `HTTP ${res.statusCode ?? \"?\"}: ${raw.slice(0, 200)}`,\n );\n (err as NodeJS.ErrnoException).code =\n res.statusCode === 401 ? \"UNAUTHORIZED\" : \"HTTP_ERROR\";\n reject(err);\n return;\n }\n try {\n resolve(JSON.parse(raw) as T);\n } catch {\n reject(new Error(`Invalid JSON response from ${path}`));\n }\n });\n });\n\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(`Request to ${path} timed out`));\n });\n\n req.on(\"error\", (err) => reject(err));\n\n if (body !== null) {\n req.write(body);\n }\n req.end();\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|