@compforge/harness-toolbox 0.4.2 → 0.4.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/README.md +28 -0
- package/dist/{chunk-c6z919qb.js → chunk-5tbzf9wd.js} +1 -1
- package/dist/chunk-5zgz1z41.js +145 -0
- package/dist/{chunk-8kts470p.js → chunk-c2n8zvyn.js} +12 -6
- package/dist/chunk-yp6tcw22.js +366 -0
- package/dist/kubernetes/client.d.ts +4 -0
- package/dist/kubernetes/client.js +3 -3
- package/dist/kubernetes/compute-model.d.ts +115 -0
- package/dist/kubernetes/compute-model.js +0 -0
- package/dist/kubernetes/compute-quantity.d.ts +18 -0
- package/dist/kubernetes/compute-quantity.js +23 -0
- package/dist/kubernetes/compute.d.ts +17 -0
- package/dist/kubernetes/compute.js +211 -0
- package/dist/kubernetes/environment.js +3 -3
- package/dist/redis/client.d.ts +27 -8
- package/dist/redis/client.js +9 -1
- package/dist/redis/index.js +11 -4
- package/dist/redis/topology.d.ts +7 -4
- package/dist/redis/topology.js +1 -1
- package/package.json +4 -1
- package/dist/chunk-ajhdd4hp.js +0 -267
package/dist/chunk-ajhdd4hp.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
// src/redis/client.ts
|
|
2
|
-
import { createClient } from "@redis/client";
|
|
3
|
-
function stringValue(value) {
|
|
4
|
-
if (Buffer.isBuffer(value))
|
|
5
|
-
return value.toString("utf8");
|
|
6
|
-
return String(value ?? "");
|
|
7
|
-
}
|
|
8
|
-
function scalar(value) {
|
|
9
|
-
if (/^-?(?:\d+|\d+\.\d+)$/.test(value))
|
|
10
|
-
return Number(value);
|
|
11
|
-
return value;
|
|
12
|
-
}
|
|
13
|
-
function parseRedisInfo(raw) {
|
|
14
|
-
const result = {};
|
|
15
|
-
for (const line of raw.split(/\r?\n/)) {
|
|
16
|
-
if (!line || line.startsWith("#"))
|
|
17
|
-
continue;
|
|
18
|
-
const separator = line.indexOf(":");
|
|
19
|
-
if (separator < 1)
|
|
20
|
-
continue;
|
|
21
|
-
const name = line.slice(0, separator);
|
|
22
|
-
const value = line.slice(separator + 1);
|
|
23
|
-
if (/^db\d+$/.test(name)) {
|
|
24
|
-
result[name] = Object.fromEntries(value.split(",").map((item) => {
|
|
25
|
-
const index = item.indexOf("=");
|
|
26
|
-
return [item.slice(0, index), scalar(item.slice(index + 1))];
|
|
27
|
-
}));
|
|
28
|
-
} else {
|
|
29
|
-
result[name] = scalar(value);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
class RedisConnection {
|
|
36
|
-
endpoint;
|
|
37
|
-
client;
|
|
38
|
-
timeoutMs;
|
|
39
|
-
lastError;
|
|
40
|
-
constructor(endpoint, client, timeoutMs, lastError) {
|
|
41
|
-
this.endpoint = endpoint;
|
|
42
|
-
this.client = client;
|
|
43
|
-
this.timeoutMs = timeoutMs;
|
|
44
|
-
this.lastError = lastError;
|
|
45
|
-
}
|
|
46
|
-
static async connect(endpoint, mapped, config, clientFactory = createClient) {
|
|
47
|
-
const baseSocket = {
|
|
48
|
-
host: mapped.host,
|
|
49
|
-
port: mapped.port,
|
|
50
|
-
connectTimeout: config.timeoutMs,
|
|
51
|
-
reconnectStrategy: (retries) => retries >= 2 ? false : 100 * 2 ** retries
|
|
52
|
-
};
|
|
53
|
-
const socket = config.useSsl ? {
|
|
54
|
-
...baseSocket,
|
|
55
|
-
tls: true,
|
|
56
|
-
servername: mapped.servername ?? endpoint.host
|
|
57
|
-
} : {
|
|
58
|
-
...baseSocket,
|
|
59
|
-
tls: false
|
|
60
|
-
};
|
|
61
|
-
const client = clientFactory({
|
|
62
|
-
socket,
|
|
63
|
-
database: config.database,
|
|
64
|
-
username: config.username,
|
|
65
|
-
password: config.password,
|
|
66
|
-
commandOptions: { timeout: config.timeoutMs }
|
|
67
|
-
});
|
|
68
|
-
let lastError;
|
|
69
|
-
client.on("error", (err) => {
|
|
70
|
-
lastError = err instanceof Error ? err : new Error(String(err));
|
|
71
|
-
});
|
|
72
|
-
try {
|
|
73
|
-
await client.connect();
|
|
74
|
-
} catch (error) {
|
|
75
|
-
if (client.isOpen)
|
|
76
|
-
client.destroy();
|
|
77
|
-
throw error;
|
|
78
|
-
}
|
|
79
|
-
return new RedisConnection(endpoint, client, config.timeoutMs, () => lastError);
|
|
80
|
-
}
|
|
81
|
-
get isReady() {
|
|
82
|
-
return this.client.isReady;
|
|
83
|
-
}
|
|
84
|
-
async#execute(operationName, operation) {
|
|
85
|
-
try {
|
|
86
|
-
return await this.#executeOnce(operationName, operation);
|
|
87
|
-
} catch (err) {
|
|
88
|
-
if (this.client.isOpen && !this.client.isReady)
|
|
89
|
-
return this.#executeOnce(operationName, operation);
|
|
90
|
-
throw err;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
async#executeOnce(operationName, operation) {
|
|
94
|
-
if (!this.client.isOpen) {
|
|
95
|
-
const reason = this.lastError()?.message;
|
|
96
|
-
throw new Error(`Redis connection is closed${reason ? `: ${reason}` : ""}`);
|
|
97
|
-
}
|
|
98
|
-
let timer;
|
|
99
|
-
const deadline = new Promise((_, reject) => {
|
|
100
|
-
timer = setTimeout(() => {
|
|
101
|
-
reject(new Error(`Redis ${operationName} timed out after ${this.timeoutMs}ms (${this.endpoint.host}:${this.endpoint.port})`));
|
|
102
|
-
this.close();
|
|
103
|
-
}, this.timeoutMs);
|
|
104
|
-
});
|
|
105
|
-
try {
|
|
106
|
-
return await Promise.race([Promise.resolve().then(operation), deadline]);
|
|
107
|
-
} finally {
|
|
108
|
-
if (timer)
|
|
109
|
-
clearTimeout(timer);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
ping() {
|
|
113
|
-
return this.#execute("PING", () => this.client.ping());
|
|
114
|
-
}
|
|
115
|
-
async info(section) {
|
|
116
|
-
const raw = await this.#execute(section ? `INFO ${section}` : "INFO", () => section ? this.client.sendCommand(["INFO", section]) : this.client.sendCommand(["INFO"]));
|
|
117
|
-
return parseRedisInfo(stringValue(raw));
|
|
118
|
-
}
|
|
119
|
-
async dbSize() {
|
|
120
|
-
return Number(await this.#execute("DBSIZE", () => this.client.sendCommand(["DBSIZE"])));
|
|
121
|
-
}
|
|
122
|
-
command(args) {
|
|
123
|
-
return this.#execute(args[0]?.toUpperCase() ?? "command", () => this.client.sendCommand(args));
|
|
124
|
-
}
|
|
125
|
-
async pipeline(commands) {
|
|
126
|
-
const commandNames = new Set(commands.map((command) => command[0]?.toUpperCase()).filter(Boolean));
|
|
127
|
-
const operationName = commandNames.size === 1 ? `${[...commandNames][0]} pipeline` : "pipeline";
|
|
128
|
-
return await this.#execute(operationName, () => {
|
|
129
|
-
const pipeline = this.client.multi();
|
|
130
|
-
for (const command of commands)
|
|
131
|
-
pipeline.addCommand(command);
|
|
132
|
-
return pipeline.execAsPipeline();
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
async scan(cursor, count) {
|
|
136
|
-
const reply = await this.command(["SCAN", cursor, "COUNT", String(count)]);
|
|
137
|
-
if (!Array.isArray(reply) || !Array.isArray(reply[1]))
|
|
138
|
-
throw new Error("Redis SCAN 返回格式无效");
|
|
139
|
-
return { cursor: stringValue(reply[0]), keys: reply[1].map(stringValue) };
|
|
140
|
-
}
|
|
141
|
-
close() {
|
|
142
|
-
if (this.client.isOpen)
|
|
143
|
-
this.client.destroy();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
class RedisAccess {
|
|
148
|
-
transport;
|
|
149
|
-
baseConfig;
|
|
150
|
-
connectionFactory;
|
|
151
|
-
#connections = new Map;
|
|
152
|
-
#closing;
|
|
153
|
-
constructor(transport, baseConfig, connectionFactory = RedisConnection.connect) {
|
|
154
|
-
this.transport = transport;
|
|
155
|
-
this.baseConfig = baseConfig;
|
|
156
|
-
this.connectionFactory = connectionFactory;
|
|
157
|
-
}
|
|
158
|
-
async connection(endpoint, database, credentials = this.baseConfig) {
|
|
159
|
-
if (this.#closing)
|
|
160
|
-
throw new Error("Redis access is closed");
|
|
161
|
-
const key = [
|
|
162
|
-
endpoint.host,
|
|
163
|
-
endpoint.port,
|
|
164
|
-
database,
|
|
165
|
-
credentials.username ?? "",
|
|
166
|
-
credentials.password ?? ""
|
|
167
|
-
].join("\x00");
|
|
168
|
-
let pending = this.#connections.get(key);
|
|
169
|
-
if (pending) {
|
|
170
|
-
const connection = await pending;
|
|
171
|
-
if (this.#closing)
|
|
172
|
-
throw new Error("Redis access is closed");
|
|
173
|
-
if (connection.isReady)
|
|
174
|
-
return connection;
|
|
175
|
-
if (this.#connections.get(key) === pending) {
|
|
176
|
-
this.#connections.delete(key);
|
|
177
|
-
connection.close();
|
|
178
|
-
} else {
|
|
179
|
-
return this.connection(endpoint, database, credentials);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
pending = this.transport.connect(endpoint).then((mapped) => this.connectionFactory(endpoint, mapped, {
|
|
183
|
-
...this.baseConfig,
|
|
184
|
-
...credentials,
|
|
185
|
-
database
|
|
186
|
-
}));
|
|
187
|
-
this.#connections.set(key, pending);
|
|
188
|
-
pending.catch(() => {
|
|
189
|
-
if (this.#connections.get(key) === pending)
|
|
190
|
-
this.#connections.delete(key);
|
|
191
|
-
});
|
|
192
|
-
return await pending;
|
|
193
|
-
}
|
|
194
|
-
close() {
|
|
195
|
-
return this.#closing ??= (async () => {
|
|
196
|
-
const settled = await Promise.allSettled(this.#connections.values());
|
|
197
|
-
this.#connections.clear();
|
|
198
|
-
const errors = [];
|
|
199
|
-
for (const result of settled)
|
|
200
|
-
if (result.status === "fulfilled") {
|
|
201
|
-
try {
|
|
202
|
-
result.value.close();
|
|
203
|
-
} catch (error) {
|
|
204
|
-
errors.push(error);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
if (errors.length)
|
|
208
|
-
throw new AggregateError(errors, "Redis connection cleanup failed");
|
|
209
|
-
})();
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
class RedisClient {
|
|
214
|
-
source;
|
|
215
|
-
#controller = new AbortController;
|
|
216
|
-
signal;
|
|
217
|
-
#initialization;
|
|
218
|
-
#disposal;
|
|
219
|
-
#access;
|
|
220
|
-
#target;
|
|
221
|
-
constructor(source, lifecycle = {}) {
|
|
222
|
-
this.source = source;
|
|
223
|
-
this.signal = lifecycle.signal ? AbortSignal.any([lifecycle.signal, this.#controller.signal]) : this.#controller.signal;
|
|
224
|
-
}
|
|
225
|
-
initialize() {
|
|
226
|
-
this.signal.throwIfAborted();
|
|
227
|
-
return this.#initialization ??= (async () => {
|
|
228
|
-
this.#target = await this.source.resolve();
|
|
229
|
-
this.signal.throwIfAborted();
|
|
230
|
-
const transport = this.source.transports[0];
|
|
231
|
-
if (!transport || transport.kind !== "tcp")
|
|
232
|
-
throw new Error("Redis requires a TCP transport");
|
|
233
|
-
this.#access = new RedisAccess(transport, this.#target);
|
|
234
|
-
})();
|
|
235
|
-
}
|
|
236
|
-
get access() {
|
|
237
|
-
this.signal.throwIfAborted();
|
|
238
|
-
if (!this.#access)
|
|
239
|
-
throw new Error("Redis client is not initialized");
|
|
240
|
-
return this.#access;
|
|
241
|
-
}
|
|
242
|
-
get target() {
|
|
243
|
-
if (!this.#target)
|
|
244
|
-
throw new Error("Redis client is not initialized");
|
|
245
|
-
return this.#target;
|
|
246
|
-
}
|
|
247
|
-
dispose() {
|
|
248
|
-
return this.#disposal ??= (async () => {
|
|
249
|
-
this.#controller.abort(new Error("Redis client disposed"));
|
|
250
|
-
await this.#initialization?.catch(() => {});
|
|
251
|
-
await this.#access?.close();
|
|
252
|
-
})();
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
async function openRedis(source, lifecycle = {}) {
|
|
256
|
-
const client = new RedisClient(source, lifecycle);
|
|
257
|
-
try {
|
|
258
|
-
await client.initialize();
|
|
259
|
-
} catch (error) {
|
|
260
|
-
await client.dispose();
|
|
261
|
-
throw error;
|
|
262
|
-
}
|
|
263
|
-
lifecycle.onDispose?.(() => client.dispose());
|
|
264
|
-
return client;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
export { parseRedisInfo, RedisConnection, RedisAccess, RedisClient, openRedis };
|