modality-kit 0.6.5 → 0.7.1
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/dist/index.js +442 -1
- package/dist/types/__tests__/pending-operations.test.d.ts +12 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/schemas/jsonrpc.d.ts +212 -0
- package/dist/types/util_pending.d.ts +330 -0
- package/dist/types/util_tests/TimerMockManager.d.ts +63 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4267,6 +4267,174 @@ var fileEntrySchema = exports_external.object({
|
|
|
4267
4267
|
type: exports_external.enum(["file", "directory"]).describe("Entry type"),
|
|
4268
4268
|
lastModified: exports_external.number().optional().nullable().describe("Last modified timestamp in milliseconds since epoch")
|
|
4269
4269
|
});
|
|
4270
|
+
// src/schemas/jsonrpc.ts
|
|
4271
|
+
var exports_jsonrpc = {};
|
|
4272
|
+
__export(exports_jsonrpc, {
|
|
4273
|
+
STANDARD_ERROR_MESSAGES: () => STANDARD_ERROR_MESSAGES,
|
|
4274
|
+
JSONRPC_VERSION: () => JSONRPC_VERSION,
|
|
4275
|
+
JSONRPCUtils: () => JSONRPCUtils,
|
|
4276
|
+
JSONRPCErrorCode: () => JSONRPCErrorCode
|
|
4277
|
+
});
|
|
4278
|
+
var JSONRPC_VERSION = "2.0";
|
|
4279
|
+
var JSONRPCErrorCode;
|
|
4280
|
+
((JSONRPCErrorCode2) => {
|
|
4281
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
|
|
4282
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
|
|
4283
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND";
|
|
4284
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
|
|
4285
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
|
|
4286
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["SERVER_ERROR_START"] = -32099] = "SERVER_ERROR_START";
|
|
4287
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["SERVER_ERROR_END"] = -32000] = "SERVER_ERROR_END";
|
|
4288
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["TIMEOUT_ERROR"] = -32001] = "TIMEOUT_ERROR";
|
|
4289
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["CONNECTION_ERROR"] = -32002] = "CONNECTION_ERROR";
|
|
4290
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["AUTHENTICATION_ERROR"] = -32003] = "AUTHENTICATION_ERROR";
|
|
4291
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["AUTHORIZATION_ERROR"] = -32004] = "AUTHORIZATION_ERROR";
|
|
4292
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["RATE_LIMIT_ERROR"] = -32005] = "RATE_LIMIT_ERROR";
|
|
4293
|
+
JSONRPCErrorCode2[JSONRPCErrorCode2["VALIDATION_ERROR"] = -32006] = "VALIDATION_ERROR";
|
|
4294
|
+
})(JSONRPCErrorCode ||= {});
|
|
4295
|
+
|
|
4296
|
+
class JSONRPCUtils {
|
|
4297
|
+
static validateMessage(message) {
|
|
4298
|
+
if (Array.isArray(message)) {
|
|
4299
|
+
if (message.length === 0) {
|
|
4300
|
+
return {
|
|
4301
|
+
valid: false,
|
|
4302
|
+
error: {
|
|
4303
|
+
code: -32600 /* INVALID_REQUEST */,
|
|
4304
|
+
message: "Invalid request: batch array cannot be empty"
|
|
4305
|
+
}
|
|
4306
|
+
};
|
|
4307
|
+
}
|
|
4308
|
+
for (const item of message) {
|
|
4309
|
+
const itemValidation = this.validateSingleMessage(item);
|
|
4310
|
+
if (!itemValidation.valid) {
|
|
4311
|
+
return itemValidation;
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
return {
|
|
4315
|
+
valid: true,
|
|
4316
|
+
messageType: "batch"
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
return this.validateSingleMessage(message);
|
|
4320
|
+
}
|
|
4321
|
+
static validateSingleMessage(message) {
|
|
4322
|
+
if (!message || typeof message !== "object") {
|
|
4323
|
+
return {
|
|
4324
|
+
valid: false,
|
|
4325
|
+
error: {
|
|
4326
|
+
code: -32600 /* INVALID_REQUEST */,
|
|
4327
|
+
message: "Invalid request: message must be an object"
|
|
4328
|
+
}
|
|
4329
|
+
};
|
|
4330
|
+
}
|
|
4331
|
+
if (message.jsonrpc !== JSONRPC_VERSION) {
|
|
4332
|
+
return {
|
|
4333
|
+
valid: false,
|
|
4334
|
+
error: {
|
|
4335
|
+
code: -32600 /* INVALID_REQUEST */,
|
|
4336
|
+
message: `Invalid request: expect ${JSONRPC_VERSION}, received ${message.jsonrpc}`
|
|
4337
|
+
}
|
|
4338
|
+
};
|
|
4339
|
+
}
|
|
4340
|
+
if (!message.method || typeof message.method !== "string") {
|
|
4341
|
+
if ("result" in message || "error" in message) {
|
|
4342
|
+
return {
|
|
4343
|
+
valid: true,
|
|
4344
|
+
messageType: "response"
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
4347
|
+
return {
|
|
4348
|
+
valid: false,
|
|
4349
|
+
error: {
|
|
4350
|
+
code: -32600 /* INVALID_REQUEST */,
|
|
4351
|
+
message: "Invalid request: method must be a string"
|
|
4352
|
+
}
|
|
4353
|
+
};
|
|
4354
|
+
}
|
|
4355
|
+
const hasId = "id" in message;
|
|
4356
|
+
const messageType = hasId ? "request" : "notification";
|
|
4357
|
+
return {
|
|
4358
|
+
valid: true,
|
|
4359
|
+
messageType
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
static createRequest(method, params, id) {
|
|
4363
|
+
return {
|
|
4364
|
+
jsonrpc: JSONRPC_VERSION,
|
|
4365
|
+
method,
|
|
4366
|
+
params,
|
|
4367
|
+
id: id ?? this.generateId()
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
static createNotification(method, params) {
|
|
4371
|
+
return {
|
|
4372
|
+
jsonrpc: JSONRPC_VERSION,
|
|
4373
|
+
method,
|
|
4374
|
+
params
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
4377
|
+
static createSuccessResponse(result, id) {
|
|
4378
|
+
return {
|
|
4379
|
+
jsonrpc: JSONRPC_VERSION,
|
|
4380
|
+
result,
|
|
4381
|
+
id
|
|
4382
|
+
};
|
|
4383
|
+
}
|
|
4384
|
+
static createErrorResponse(error, id) {
|
|
4385
|
+
return {
|
|
4386
|
+
jsonrpc: JSONRPC_VERSION,
|
|
4387
|
+
error,
|
|
4388
|
+
id
|
|
4389
|
+
};
|
|
4390
|
+
}
|
|
4391
|
+
static createError(code, message, data) {
|
|
4392
|
+
return { code, message, data };
|
|
4393
|
+
}
|
|
4394
|
+
static generateId() {
|
|
4395
|
+
return Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
4396
|
+
}
|
|
4397
|
+
static isRequest(message) {
|
|
4398
|
+
return message && message.jsonrpc === JSONRPC_VERSION && typeof message.method === "string" && "id" in message;
|
|
4399
|
+
}
|
|
4400
|
+
static isNotification(message) {
|
|
4401
|
+
return message && message.jsonrpc === JSONRPC_VERSION && typeof message.method === "string" && !("id" in message);
|
|
4402
|
+
}
|
|
4403
|
+
static isResponse(message) {
|
|
4404
|
+
return message && message.jsonrpc === JSONRPC_VERSION && (("result" in message) || ("error" in message)) && "id" in message;
|
|
4405
|
+
}
|
|
4406
|
+
static isSuccessResponse(response) {
|
|
4407
|
+
return "result" in response;
|
|
4408
|
+
}
|
|
4409
|
+
static isErrorResponse(response) {
|
|
4410
|
+
return "error" in response;
|
|
4411
|
+
}
|
|
4412
|
+
static serialize(message) {
|
|
4413
|
+
return JSON.stringify(message);
|
|
4414
|
+
}
|
|
4415
|
+
static deserialize(data) {
|
|
4416
|
+
try {
|
|
4417
|
+
return JSON.parse(data);
|
|
4418
|
+
} catch {
|
|
4419
|
+
return null;
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
}
|
|
4423
|
+
var STANDARD_ERROR_MESSAGES = {
|
|
4424
|
+
[-32700 /* PARSE_ERROR */]: "Parse error",
|
|
4425
|
+
[-32600 /* INVALID_REQUEST */]: "Invalid Request",
|
|
4426
|
+
[-32601 /* METHOD_NOT_FOUND */]: "Method not found",
|
|
4427
|
+
[-32602 /* INVALID_PARAMS */]: "Invalid params",
|
|
4428
|
+
[-32603 /* INTERNAL_ERROR */]: "Internal error",
|
|
4429
|
+
[-32099 /* SERVER_ERROR_START */]: "Server error",
|
|
4430
|
+
[-32000 /* SERVER_ERROR_END */]: "Server error",
|
|
4431
|
+
[-32001 /* TIMEOUT_ERROR */]: "Request timeout",
|
|
4432
|
+
[-32002 /* CONNECTION_ERROR */]: "Connection error",
|
|
4433
|
+
[-32003 /* AUTHENTICATION_ERROR */]: "Authentication required",
|
|
4434
|
+
[-32004 /* AUTHORIZATION_ERROR */]: "Authorization failed",
|
|
4435
|
+
[-32005 /* RATE_LIMIT_ERROR */]: "Rate limit exceeded",
|
|
4436
|
+
[-32006 /* VALIDATION_ERROR */]: "Validation error"
|
|
4437
|
+
};
|
|
4270
4438
|
// src/schemas/schemas_empty.ts
|
|
4271
4439
|
var emptySchema = exports_external.object({}).describe("No parameters required");
|
|
4272
4440
|
// src/util_version.ts
|
|
@@ -4989,6 +5157,276 @@ async function compressWithLanguageDetection(text, maxTokens = DEFAULT_CONFIG.ma
|
|
|
4989
5157
|
compressionLevel: "moderate"
|
|
4990
5158
|
});
|
|
4991
5159
|
}
|
|
5160
|
+
// src/util_pending.ts
|
|
5161
|
+
var getUUID = () => crypto.randomUUID();
|
|
5162
|
+
|
|
5163
|
+
class PendingOperationsBase {
|
|
5164
|
+
operations = new Map;
|
|
5165
|
+
config;
|
|
5166
|
+
cleanupIntervalHandle;
|
|
5167
|
+
eventHandlers = {};
|
|
5168
|
+
constructor(config = {}, eventHandlers = {}) {
|
|
5169
|
+
this.config = {
|
|
5170
|
+
defaultTimeout: 30000,
|
|
5171
|
+
cleanupInterval: 1e4,
|
|
5172
|
+
enableAutoCleanup: true,
|
|
5173
|
+
generateId: getUUID,
|
|
5174
|
+
...config
|
|
5175
|
+
};
|
|
5176
|
+
this.eventHandlers = eventHandlers;
|
|
5177
|
+
if (this.config.enableAutoCleanup) {
|
|
5178
|
+
this.startAutoCleanup();
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
resolve(id, result) {
|
|
5182
|
+
const operation = this.operations.get(id);
|
|
5183
|
+
if (!operation) {
|
|
5184
|
+
return false;
|
|
5185
|
+
}
|
|
5186
|
+
if (operation.timeoutHandle) {
|
|
5187
|
+
clearTimeout(operation.timeoutHandle);
|
|
5188
|
+
}
|
|
5189
|
+
if (operation.type === "promise") {
|
|
5190
|
+
operation.resolve(result);
|
|
5191
|
+
}
|
|
5192
|
+
if (this.eventHandlers.onResolve) {
|
|
5193
|
+
this.eventHandlers.onResolve(operation, result);
|
|
5194
|
+
}
|
|
5195
|
+
this.operations.delete(id);
|
|
5196
|
+
return true;
|
|
5197
|
+
}
|
|
5198
|
+
reject(id, reason) {
|
|
5199
|
+
const operation = this.operations.get(id);
|
|
5200
|
+
if (!operation) {
|
|
5201
|
+
return false;
|
|
5202
|
+
}
|
|
5203
|
+
if (operation.timeoutHandle) {
|
|
5204
|
+
clearTimeout(operation.timeoutHandle);
|
|
5205
|
+
}
|
|
5206
|
+
if (operation.type === "promise") {
|
|
5207
|
+
operation.reject(reason);
|
|
5208
|
+
}
|
|
5209
|
+
if (this.eventHandlers.onReject) {
|
|
5210
|
+
this.eventHandlers.onReject(operation, reason);
|
|
5211
|
+
}
|
|
5212
|
+
this.operations.delete(id);
|
|
5213
|
+
return true;
|
|
5214
|
+
}
|
|
5215
|
+
get(id) {
|
|
5216
|
+
return this.operations.get(id);
|
|
5217
|
+
}
|
|
5218
|
+
has(id) {
|
|
5219
|
+
return this.operations.has(id);
|
|
5220
|
+
}
|
|
5221
|
+
remove(id) {
|
|
5222
|
+
const operation = this.operations.get(id);
|
|
5223
|
+
if (!operation) {
|
|
5224
|
+
return false;
|
|
5225
|
+
}
|
|
5226
|
+
if (operation.timeoutHandle) {
|
|
5227
|
+
clearTimeout(operation.timeoutHandle);
|
|
5228
|
+
}
|
|
5229
|
+
if (this.eventHandlers.onCleanup) {
|
|
5230
|
+
this.eventHandlers.onCleanup(operation);
|
|
5231
|
+
}
|
|
5232
|
+
this.operations.delete(id);
|
|
5233
|
+
return true;
|
|
5234
|
+
}
|
|
5235
|
+
getAll() {
|
|
5236
|
+
return new Map(this.operations);
|
|
5237
|
+
}
|
|
5238
|
+
getByType(type) {
|
|
5239
|
+
return Array.from(this.operations.values()).filter((op) => op.type === type);
|
|
5240
|
+
}
|
|
5241
|
+
clear(reason) {
|
|
5242
|
+
const count = this.operations.size;
|
|
5243
|
+
const keys = Array.from(this.operations.keys());
|
|
5244
|
+
for (const id of keys) {
|
|
5245
|
+
this.reject(id, reason || "All operations cleared");
|
|
5246
|
+
}
|
|
5247
|
+
return count;
|
|
5248
|
+
}
|
|
5249
|
+
cleanupExpired() {
|
|
5250
|
+
const now = Date.now();
|
|
5251
|
+
const expiredOperations = [];
|
|
5252
|
+
for (const [id, operation] of Array.from(this.operations.entries())) {
|
|
5253
|
+
if (operation.timeout && operation.timeout > 0) {
|
|
5254
|
+
const expirationTime = operation.timestamp + operation.timeout;
|
|
5255
|
+
if (now >= expirationTime) {
|
|
5256
|
+
expiredOperations.push(id);
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
5259
|
+
}
|
|
5260
|
+
for (const id of expiredOperations) {
|
|
5261
|
+
this.handleTimeout(this.operations.get(id));
|
|
5262
|
+
}
|
|
5263
|
+
return expiredOperations.length;
|
|
5264
|
+
}
|
|
5265
|
+
getStats() {
|
|
5266
|
+
const now = Date.now();
|
|
5267
|
+
const operations = Array.from(this.operations.values());
|
|
5268
|
+
const byType = {};
|
|
5269
|
+
let totalAge = 0;
|
|
5270
|
+
let expiringSoon = 0;
|
|
5271
|
+
let oldestTimestamp;
|
|
5272
|
+
for (const operation of operations) {
|
|
5273
|
+
byType[operation.type] = (byType[operation.type] || 0) + 1;
|
|
5274
|
+
const age = now - operation.timestamp;
|
|
5275
|
+
totalAge += age;
|
|
5276
|
+
if (!oldestTimestamp || operation.timestamp < oldestTimestamp) {
|
|
5277
|
+
oldestTimestamp = operation.timestamp;
|
|
5278
|
+
}
|
|
5279
|
+
if (operation.timeout && operation.timeout > 0) {
|
|
5280
|
+
const expirationTime = operation.timestamp + operation.timeout;
|
|
5281
|
+
const timeToExpiry = expirationTime - now;
|
|
5282
|
+
if (timeToExpiry <= 60000 && timeToExpiry > 0) {
|
|
5283
|
+
expiringSoon++;
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
return {
|
|
5288
|
+
total: operations.length,
|
|
5289
|
+
byType,
|
|
5290
|
+
expiringSoon,
|
|
5291
|
+
oldestTimestamp,
|
|
5292
|
+
averageAge: operations.length > 0 ? totalAge / operations.length : 0
|
|
5293
|
+
};
|
|
5294
|
+
}
|
|
5295
|
+
startAutoCleanup() {
|
|
5296
|
+
if (this.cleanupIntervalHandle) {
|
|
5297
|
+
return;
|
|
5298
|
+
}
|
|
5299
|
+
this.cleanupIntervalHandle = setInterval(() => {
|
|
5300
|
+
this.cleanupExpired();
|
|
5301
|
+
}, this.config.cleanupInterval);
|
|
5302
|
+
}
|
|
5303
|
+
stopAutoCleanup() {
|
|
5304
|
+
if (this.cleanupIntervalHandle) {
|
|
5305
|
+
clearInterval(this.cleanupIntervalHandle);
|
|
5306
|
+
this.cleanupIntervalHandle = undefined;
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
handleTimeout({ id }) {
|
|
5310
|
+
const operation = this.operations.get(id);
|
|
5311
|
+
if (operation) {
|
|
5312
|
+
if (this.eventHandlers.onTimeout) {
|
|
5313
|
+
this.eventHandlers.onTimeout(operation);
|
|
5314
|
+
}
|
|
5315
|
+
this.reject(operation.id, new Error(`Operation timed out after ${operation.timeout}ms`));
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
destroy(reason) {
|
|
5319
|
+
this.stopAutoCleanup();
|
|
5320
|
+
this.clear(reason || "PendingOperations destroyed");
|
|
5321
|
+
}
|
|
5322
|
+
getConfig() {
|
|
5323
|
+
return { ...this.config };
|
|
5324
|
+
}
|
|
5325
|
+
setEventHandlers(handlers) {
|
|
5326
|
+
this.eventHandlers = { ...this.eventHandlers, ...handlers };
|
|
5327
|
+
}
|
|
5328
|
+
handleAdd(options = {}) {
|
|
5329
|
+
const id = options.customId ?? this.config.generateId();
|
|
5330
|
+
const timeout = options.timeout ?? this.config.defaultTimeout;
|
|
5331
|
+
if (options.customId && this.operations.has(options.customId)) {
|
|
5332
|
+
throw new Error(`Operation with ID '${options.customId}' already exists`);
|
|
5333
|
+
}
|
|
5334
|
+
const timeoutHandle = timeout > 0 ? setTimeout(() => {
|
|
5335
|
+
this.handleTimeout({ id });
|
|
5336
|
+
}, timeout) : undefined;
|
|
5337
|
+
return { id, timeout, timeoutHandle };
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
|
|
5341
|
+
class PromisePendingOperations extends PendingOperationsBase {
|
|
5342
|
+
add(data, options = {}) {
|
|
5343
|
+
const { id, timeout, timeoutHandle } = this.handleAdd(options);
|
|
5344
|
+
const promise = new Promise((resolve, reject) => {
|
|
5345
|
+
const operation = {
|
|
5346
|
+
id,
|
|
5347
|
+
type: "promise",
|
|
5348
|
+
timestamp: Date.now(),
|
|
5349
|
+
timeout,
|
|
5350
|
+
timeoutHandle,
|
|
5351
|
+
data,
|
|
5352
|
+
resolve,
|
|
5353
|
+
reject
|
|
5354
|
+
};
|
|
5355
|
+
this.operations.set(id, operation);
|
|
5356
|
+
});
|
|
5357
|
+
return { id, promise };
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
|
|
5361
|
+
class DataPendingOperations extends PendingOperationsBase {
|
|
5362
|
+
add(data, options = {}) {
|
|
5363
|
+
const { id, timeout, timeoutHandle } = this.handleAdd(options);
|
|
5364
|
+
const operation = {
|
|
5365
|
+
id,
|
|
5366
|
+
type: "data",
|
|
5367
|
+
timestamp: Date.now(),
|
|
5368
|
+
timeout,
|
|
5369
|
+
timeoutHandle,
|
|
5370
|
+
data
|
|
5371
|
+
};
|
|
5372
|
+
this.operations.set(id, operation);
|
|
5373
|
+
return { id };
|
|
5374
|
+
}
|
|
5375
|
+
}
|
|
5376
|
+
function createPromisePendingOperations(config, eventHandlers) {
|
|
5377
|
+
return new PromisePendingOperations({
|
|
5378
|
+
defaultTimeout: 30000,
|
|
5379
|
+
cleanupInterval: 30000,
|
|
5380
|
+
enableAutoCleanup: true,
|
|
5381
|
+
...config
|
|
5382
|
+
}, eventHandlers);
|
|
5383
|
+
}
|
|
5384
|
+
function createDataPendingOperations(config, eventHandlers) {
|
|
5385
|
+
return new DataPendingOperations({
|
|
5386
|
+
defaultTimeout: 30000,
|
|
5387
|
+
cleanupInterval: 30000,
|
|
5388
|
+
enableAutoCleanup: true,
|
|
5389
|
+
...config
|
|
5390
|
+
}, eventHandlers);
|
|
5391
|
+
}
|
|
5392
|
+
|
|
5393
|
+
class JSONRPCCall {
|
|
5394
|
+
pendingRequests;
|
|
5395
|
+
constructor(config = {}) {
|
|
5396
|
+
const pendingEventHandlers = {
|
|
5397
|
+
onTimeout: (operation) => {
|
|
5398
|
+
console.warn(`JSON-RPC operation timed out:`, operation.id);
|
|
5399
|
+
},
|
|
5400
|
+
onResolve: (operation, _result) => {
|
|
5401
|
+
console.log(`JSON-RPC operation resolved:`, operation.id);
|
|
5402
|
+
},
|
|
5403
|
+
onReject: (operation, reason) => {
|
|
5404
|
+
console.error(`JSON-RPC operation rejected:`, operation.id, reason);
|
|
5405
|
+
}
|
|
5406
|
+
};
|
|
5407
|
+
this.pendingRequests = createPromisePendingOperations(config, pendingEventHandlers);
|
|
5408
|
+
}
|
|
5409
|
+
handleResponse(response) {
|
|
5410
|
+
const id = response.id;
|
|
5411
|
+
if (JSONRPCUtils.isSuccessResponse(response)) {
|
|
5412
|
+
this.pendingRequests.resolve(id, response.result);
|
|
5413
|
+
} else {
|
|
5414
|
+
this.pendingRequests.reject(id, new Error(response.error.message));
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
async handleRequest(method, params, options = {}) {
|
|
5418
|
+
const { promise } = this.pendingRequests.add({ method, params }, options);
|
|
5419
|
+
return promise;
|
|
5420
|
+
}
|
|
5421
|
+
getStats() {
|
|
5422
|
+
return {
|
|
5423
|
+
pendingRequests: this.pendingRequests.getStats()
|
|
5424
|
+
};
|
|
5425
|
+
}
|
|
5426
|
+
destroy() {
|
|
5427
|
+
this.pendingRequests.destroy("JSONRPCManager destroyed");
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
4992
5430
|
export {
|
|
4993
5431
|
withErrorHandling,
|
|
4994
5432
|
setupAITools,
|
|
@@ -4997,7 +5435,10 @@ export {
|
|
|
4997
5435
|
formatSuccessResponse,
|
|
4998
5436
|
formatErrorResponse,
|
|
4999
5437
|
emptySchema,
|
|
5438
|
+
createDataPendingOperations,
|
|
5000
5439
|
compressWithLanguageDetection as compressText,
|
|
5001
|
-
exports_schemas_symbol as
|
|
5440
|
+
exports_schemas_symbol as SymbolTypes,
|
|
5441
|
+
exports_jsonrpc as JSONRPCTypes,
|
|
5442
|
+
JSONRPCCall,
|
|
5002
5443
|
ErrorCode
|
|
5003
5444
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit Tests for PendingOperations Library
|
|
3
|
+
*
|
|
4
|
+
* Tests all functionality of the PendingOperations class including:
|
|
5
|
+
* - Promise-based operations
|
|
6
|
+
* - Request-based operations
|
|
7
|
+
* - Context-based operations
|
|
8
|
+
* - Timeout handling
|
|
9
|
+
* - Cleanup functionality
|
|
10
|
+
* - Statistics and monitoring
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ export type { AITools, AITool } from "./schemas/schemas_tool_config";
|
|
|
3
3
|
export { formatErrorResponse, formatSuccessResponse } from "./util_response";
|
|
4
4
|
export { getLoggerInstance, type ModalityLogger } from "./util_logger";
|
|
5
5
|
export { withErrorHandling, ErrorCode } from "./util_error";
|
|
6
|
-
export * as
|
|
6
|
+
export * as SymbolTypes from "./schemas/schemas_symbol";
|
|
7
|
+
export * as JSONRPCTypes from "./schemas/jsonrpc";
|
|
7
8
|
export type { EmptyType } from "./schemas/schemas_empty";
|
|
8
9
|
export { emptySchema } from "./schemas/schemas_empty";
|
|
9
10
|
export { loadVersion } from "./util_version";
|
|
10
11
|
export { compressWithLanguageDetection as compressText } from "./util_text_compression";
|
|
12
|
+
export { JSONRPCCall, createDataPendingOperations } from "./util_pending";
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-RPC 2.0 Types and Interfaces
|
|
3
|
+
* @see https://www.jsonrpc.org/specification
|
|
4
|
+
*
|
|
5
|
+
* Comprehensive TypeScript definitions for JSON-RPC 2.0 specification
|
|
6
|
+
* Provides type-safe interfaces for request/response/notification patterns
|
|
7
|
+
* and utilities for message handling and validation.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Generic JSON-RPC method type for type-safe method definitions
|
|
11
|
+
* Usage: type MyMethod = JSONRPCMethod<MyParams, MyResult>
|
|
12
|
+
* Or as object: { methodName: JSONRPCMethod<MyParams, MyResult> }
|
|
13
|
+
*/
|
|
14
|
+
export type JSONRPCMethod<TParams = JSONRPCParams, TResult = any> = (method: string, params?: TParams, options?: any) => Promise<TResult>;
|
|
15
|
+
/**
|
|
16
|
+
* JSON-RPC 2.0 version identifier
|
|
17
|
+
*/
|
|
18
|
+
export declare const JSONRPC_VERSION: "2.0";
|
|
19
|
+
/**
|
|
20
|
+
* Standard JSON-RPC 2.0 error codes as defined in the specification
|
|
21
|
+
*/
|
|
22
|
+
export declare enum JSONRPCErrorCode {
|
|
23
|
+
PARSE_ERROR = -32700,
|
|
24
|
+
INVALID_REQUEST = -32600,
|
|
25
|
+
METHOD_NOT_FOUND = -32601,
|
|
26
|
+
INVALID_PARAMS = -32602,
|
|
27
|
+
INTERNAL_ERROR = -32603,
|
|
28
|
+
SERVER_ERROR_START = -32099,
|
|
29
|
+
SERVER_ERROR_END = -32000,
|
|
30
|
+
TIMEOUT_ERROR = -32001,
|
|
31
|
+
CONNECTION_ERROR = -32002,
|
|
32
|
+
AUTHENTICATION_ERROR = -32003,
|
|
33
|
+
AUTHORIZATION_ERROR = -32004,
|
|
34
|
+
RATE_LIMIT_ERROR = -32005,
|
|
35
|
+
VALIDATION_ERROR = -32006
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* JSON-RPC 2.0 parameter types - can be object, array, or null
|
|
39
|
+
*
|
|
40
|
+
* IMPORTANT: DO NOT add 'undefined' to this type!
|
|
41
|
+
*
|
|
42
|
+
* Reasoning per JSON-RPC 2.0 specification (https://www.jsonrpc.org/specification):
|
|
43
|
+
* 1. JSON specification does not support 'undefined' - only null, objects, arrays, etc.
|
|
44
|
+
* 2. JSON.stringify() omits undefined values, making {params: undefined} equivalent to {}
|
|
45
|
+
* 3. JSON-RPC 2.0 spec defines params as: Object | Array | omitted entirely
|
|
46
|
+
* 4. Use cases:
|
|
47
|
+
* - No parameters: omit 'params' field entirely
|
|
48
|
+
* - Explicit null: use 'params: null'
|
|
49
|
+
* - Empty object: use 'params: {}'
|
|
50
|
+
* - Empty array: use 'params: []'
|
|
51
|
+
*
|
|
52
|
+
* This ensures strict JSON-RPC 2.0 compliance and wire protocol compatibility.
|
|
53
|
+
* See: https://www.jsonrpc.org/specification#parameter_structures
|
|
54
|
+
*/
|
|
55
|
+
export type JSONRPCParams = object | any[] | null;
|
|
56
|
+
/**
|
|
57
|
+
* JSON-RPC 2.0 ID type - string, number, or null
|
|
58
|
+
*/
|
|
59
|
+
export type JSONRPCId = string | number | null;
|
|
60
|
+
/**
|
|
61
|
+
* JSON-RPC 2.0 Error object
|
|
62
|
+
*/
|
|
63
|
+
export interface JSONRPCError {
|
|
64
|
+
/** Error code indicating the error type */
|
|
65
|
+
code: JSONRPCErrorCode | number;
|
|
66
|
+
/** Human-readable error message */
|
|
67
|
+
message: string;
|
|
68
|
+
/** Optional additional error data */
|
|
69
|
+
data?: any;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* JSON-RPC 2.0 Request object
|
|
73
|
+
*/
|
|
74
|
+
export interface JSONRPCRequest {
|
|
75
|
+
/** JSON-RPC version identifier */
|
|
76
|
+
jsonrpc: typeof JSONRPC_VERSION;
|
|
77
|
+
/** Method name to be invoked */
|
|
78
|
+
method: string;
|
|
79
|
+
/** Optional parameters for the method */
|
|
80
|
+
params?: JSONRPCParams;
|
|
81
|
+
/** Request identifier for correlation with response */
|
|
82
|
+
id: JSONRPCId;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* JSON-RPC 2.0 Notification object (request without id)
|
|
86
|
+
*/
|
|
87
|
+
export interface JSONRPCNotification {
|
|
88
|
+
/** JSON-RPC version identifier */
|
|
89
|
+
jsonrpc: typeof JSONRPC_VERSION;
|
|
90
|
+
/** Method name to be invoked */
|
|
91
|
+
method: string;
|
|
92
|
+
/** Optional parameters for the method */
|
|
93
|
+
params?: JSONRPCParams;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* JSON-RPC 2.0 Success Response object
|
|
97
|
+
*/
|
|
98
|
+
export interface JSONRPCSuccessResponse {
|
|
99
|
+
/** JSON-RPC version identifier */
|
|
100
|
+
jsonrpc: typeof JSONRPC_VERSION;
|
|
101
|
+
/** Method result */
|
|
102
|
+
result: any;
|
|
103
|
+
/** Request identifier matching the original request */
|
|
104
|
+
id: JSONRPCId;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* JSON-RPC 2.0 Error Response object
|
|
108
|
+
*/
|
|
109
|
+
export interface JSONRPCErrorResponse {
|
|
110
|
+
/** JSON-RPC version identifier */
|
|
111
|
+
jsonrpc: typeof JSONRPC_VERSION;
|
|
112
|
+
/** Error information */
|
|
113
|
+
error: JSONRPCError;
|
|
114
|
+
/** Request identifier matching the original request */
|
|
115
|
+
id: JSONRPCId;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Union type for JSON-RPC 2.0 Response objects
|
|
119
|
+
*/
|
|
120
|
+
export type JSONRPCResponse = JSONRPCSuccessResponse | JSONRPCErrorResponse;
|
|
121
|
+
/**
|
|
122
|
+
* JSON-RPC 2.0 Batch Request (array of requests and/or notifications)
|
|
123
|
+
*/
|
|
124
|
+
export type JSONRPCBatchRequest = (JSONRPCRequest | JSONRPCNotification)[];
|
|
125
|
+
/**
|
|
126
|
+
* JSON-RPC 2.0 Batch Response (array of responses)
|
|
127
|
+
*/
|
|
128
|
+
export type JSONRPCBatchResponse = JSONRPCResponse[];
|
|
129
|
+
/**
|
|
130
|
+
* Union type for all JSON-RPC 2.0 message types
|
|
131
|
+
*/
|
|
132
|
+
export type JSONRPCMessage = JSONRPCRequest | JSONRPCNotification | JSONRPCResponse | JSONRPCBatchRequest | JSONRPCBatchResponse;
|
|
133
|
+
/**
|
|
134
|
+
* JSON-RPC message validation result
|
|
135
|
+
*/
|
|
136
|
+
export interface JSONRPCValidationResult {
|
|
137
|
+
/** Whether the message is valid */
|
|
138
|
+
valid: boolean;
|
|
139
|
+
/** Error information if validation failed */
|
|
140
|
+
error?: JSONRPCError;
|
|
141
|
+
/** Parsed message type */
|
|
142
|
+
messageType?: "request" | "notification" | "response" | "batch";
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Utility functions for JSON-RPC message handling
|
|
146
|
+
*/
|
|
147
|
+
export declare class JSONRPCUtils {
|
|
148
|
+
/**
|
|
149
|
+
* Validate a JSON-RPC message (supports batch requests)
|
|
150
|
+
*/
|
|
151
|
+
static validateMessage(message: any): JSONRPCValidationResult;
|
|
152
|
+
/**
|
|
153
|
+
* Validate a single JSON-RPC message
|
|
154
|
+
*/
|
|
155
|
+
static validateSingleMessage(message: any): JSONRPCValidationResult;
|
|
156
|
+
/**
|
|
157
|
+
* Create a JSON-RPC request
|
|
158
|
+
*/
|
|
159
|
+
static createRequest(method: string, params?: JSONRPCParams, id?: JSONRPCId): JSONRPCRequest;
|
|
160
|
+
/**
|
|
161
|
+
* Create a JSON-RPC notification
|
|
162
|
+
*/
|
|
163
|
+
static createNotification(method: string, params?: JSONRPCParams): JSONRPCNotification;
|
|
164
|
+
/**
|
|
165
|
+
* Create a JSON-RPC success response
|
|
166
|
+
*/
|
|
167
|
+
static createSuccessResponse(result: any, id: JSONRPCId): JSONRPCSuccessResponse;
|
|
168
|
+
/**
|
|
169
|
+
* Create a JSON-RPC error response
|
|
170
|
+
*/
|
|
171
|
+
static createErrorResponse(error: JSONRPCError, id: JSONRPCId): JSONRPCErrorResponse;
|
|
172
|
+
/**
|
|
173
|
+
* Create a standard JSON-RPC error
|
|
174
|
+
*/
|
|
175
|
+
static createError(code: JSONRPCErrorCode | number, message: string, data?: any): JSONRPCError;
|
|
176
|
+
/**
|
|
177
|
+
* Generate a unique ID for requests
|
|
178
|
+
*/
|
|
179
|
+
static generateId(): string;
|
|
180
|
+
/**
|
|
181
|
+
* Check if a message is a JSON-RPC request
|
|
182
|
+
*/
|
|
183
|
+
static isRequest(message: any): message is JSONRPCRequest;
|
|
184
|
+
/**
|
|
185
|
+
* Check if a message is a JSON-RPC notification
|
|
186
|
+
*/
|
|
187
|
+
static isNotification(message: any): message is JSONRPCNotification;
|
|
188
|
+
/**
|
|
189
|
+
* Check if a message is a JSON-RPC response
|
|
190
|
+
*/
|
|
191
|
+
static isResponse(message: any): message is JSONRPCResponse;
|
|
192
|
+
/**
|
|
193
|
+
* Check if a response is a success response
|
|
194
|
+
*/
|
|
195
|
+
static isSuccessResponse(response: JSONRPCResponse): response is JSONRPCSuccessResponse;
|
|
196
|
+
/**
|
|
197
|
+
* Check if a response is an error response
|
|
198
|
+
*/
|
|
199
|
+
static isErrorResponse(response: JSONRPCResponse): response is JSONRPCErrorResponse;
|
|
200
|
+
/**
|
|
201
|
+
* Serialize a JSON-RPC message to string
|
|
202
|
+
*/
|
|
203
|
+
static serialize(message: JSONRPCMessage): string;
|
|
204
|
+
/**
|
|
205
|
+
* Deserialize a JSON-RPC message from string
|
|
206
|
+
*/
|
|
207
|
+
static deserialize(data: string): JSONRPCMessage | null;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Standard error messages for common JSON-RPC errors
|
|
211
|
+
*/
|
|
212
|
+
export declare const STANDARD_ERROR_MESSAGES: Record<JSONRPCErrorCode, string>;
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sharable Pending Operations Library
|
|
3
|
+
*
|
|
4
|
+
* A generic library for managing pending operations with timeout, cleanup,
|
|
5
|
+
* and lifecycle management. Can be used on both client and server sides
|
|
6
|
+
* for WebSocket communication or any async operation tracking.
|
|
7
|
+
*/
|
|
8
|
+
import { JSONRPCResponse } from "./schemas/jsonrpc";
|
|
9
|
+
/**
|
|
10
|
+
* Configuration options for PendingOperations
|
|
11
|
+
*
|
|
12
|
+
* @interface PendingOperationsConfig
|
|
13
|
+
* @description Configures timeout behavior, cleanup intervals, and ID generation for pending operations
|
|
14
|
+
*/
|
|
15
|
+
export interface PendingOperationsConfig {
|
|
16
|
+
/**
|
|
17
|
+
* Default timeout in milliseconds for operations (default: 30000)
|
|
18
|
+
* @default 30000
|
|
19
|
+
* @description Sets the default timeout for operations that don't specify their own timeout
|
|
20
|
+
*/
|
|
21
|
+
defaultTimeout?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Cleanup interval in milliseconds for expired operations (default: 10000)
|
|
24
|
+
* @default 10000
|
|
25
|
+
* @description How often the system checks for and cleans up expired operations
|
|
26
|
+
*/
|
|
27
|
+
cleanupInterval?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Enable automatic cleanup of expired operations (default: true)
|
|
30
|
+
* @default true
|
|
31
|
+
* @description Whether to automatically clean up expired operations in the background
|
|
32
|
+
*/
|
|
33
|
+
enableAutoCleanup?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Custom ID generator function (default: randomUUIDv7)
|
|
36
|
+
* @default getUUID
|
|
37
|
+
* @description Function to generate unique IDs for operations. Must return unique strings.
|
|
38
|
+
*/
|
|
39
|
+
generateId?: () => string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Base interface for all pending operation data
|
|
43
|
+
*
|
|
44
|
+
* @interface PendingOperationBase
|
|
45
|
+
* @description Common properties shared by all pending operation types
|
|
46
|
+
*/
|
|
47
|
+
export interface PendingOperationBase {
|
|
48
|
+
/**
|
|
49
|
+
* Unique identifier for the operation
|
|
50
|
+
* @description Generated automatically by the ID generator function
|
|
51
|
+
*/
|
|
52
|
+
id: string;
|
|
53
|
+
/**
|
|
54
|
+
* Timestamp when the operation was created
|
|
55
|
+
* @description Used for age calculations and timeout management
|
|
56
|
+
*/
|
|
57
|
+
timestamp: number;
|
|
58
|
+
/**
|
|
59
|
+
* Timeout in milliseconds for this specific operation
|
|
60
|
+
* @description Overrides the default timeout if specified
|
|
61
|
+
*/
|
|
62
|
+
timeout?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Optional data for the operation
|
|
65
|
+
* @description Arbitrary data for application-specific use
|
|
66
|
+
*/
|
|
67
|
+
data?: any;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Promise-based pending operation (used for server-to-client calls)
|
|
71
|
+
*
|
|
72
|
+
* @interface PromisePendingOperation
|
|
73
|
+
* @extends PendingOperationBase
|
|
74
|
+
* @description Represents an operation that provides a Promise interface for async resolution
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* const { id, promise } = pendingOps.addPromiseOperation();
|
|
78
|
+
* promise.then(result => console.log('Success:', result));
|
|
79
|
+
* // Later: pendingOps.resolve(id, { data: 'result' });
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export interface PromisePendingOperation extends PendingOperationBase {
|
|
83
|
+
/** Operation type identifier */
|
|
84
|
+
type: "promise";
|
|
85
|
+
/**
|
|
86
|
+
* Promise resolve function
|
|
87
|
+
* @description Called when the operation completes successfully
|
|
88
|
+
*/
|
|
89
|
+
resolve: (value: any) => void;
|
|
90
|
+
/**
|
|
91
|
+
* Promise reject function
|
|
92
|
+
* @description Called when the operation fails or times out
|
|
93
|
+
*/
|
|
94
|
+
reject: (reason?: any) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Optional timeout handle for cleanup
|
|
97
|
+
* @description Internal timeout handle for automatic cleanup
|
|
98
|
+
*/
|
|
99
|
+
timeoutHandle?: NodeJS.Timeout;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Data-based pending operation (used for request tracking and context storage)
|
|
103
|
+
* Replaces both RequestPendingOperation and ContextPendingOperation
|
|
104
|
+
*
|
|
105
|
+
* @interface DataPendingOperation
|
|
106
|
+
* @extends PendingOperationBase
|
|
107
|
+
* @description Flexible operation type that can store any data payload. Use for client-server requests, context tracking, or any data-centric operations.
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* // Request-style usage
|
|
111
|
+
* const id = pendingOps.addDataOperation(
|
|
112
|
+
* { requestType: 'getUserProfile', payload: { userId: 123 } },
|
|
113
|
+
* { connectionId: 'client-001', timeout: 30000 }
|
|
114
|
+
* );
|
|
115
|
+
*
|
|
116
|
+
* // Context-style usage
|
|
117
|
+
* const id2 = pendingOps.addDataOperation(
|
|
118
|
+
* { functionName: 'processCommand', context: { command: 'export' } },
|
|
119
|
+
* { timeout: 60000 }
|
|
120
|
+
* );
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export interface DataPendingOperation extends PendingOperationBase {
|
|
124
|
+
/** Operation type identifier */
|
|
125
|
+
type: "data";
|
|
126
|
+
/**
|
|
127
|
+
* Arbitrary data payload for the operation
|
|
128
|
+
* @description Can contain requestType/payload, functionName/context, connectionId, connection, or any other data structure
|
|
129
|
+
*/
|
|
130
|
+
data: any;
|
|
131
|
+
/**
|
|
132
|
+
* Optional timeout handle for cleanup
|
|
133
|
+
* @description Internal timeout handle for automatic cleanup
|
|
134
|
+
*/
|
|
135
|
+
timeoutHandle?: NodeJS.Timeout;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Union type for all pending operation types
|
|
139
|
+
*/
|
|
140
|
+
export type PendingOperation = PromisePendingOperation | DataPendingOperation;
|
|
141
|
+
/**
|
|
142
|
+
* Event handlers for pending operations
|
|
143
|
+
*/
|
|
144
|
+
export interface PendingOperationEventHandlers {
|
|
145
|
+
/** Called when an operation times out */
|
|
146
|
+
onTimeout?: (operation: PendingOperation) => void;
|
|
147
|
+
/** Called when an operation is resolved */
|
|
148
|
+
onResolve?: (operation: PendingOperation, result?: any) => void;
|
|
149
|
+
/** Called when an operation is rejected */
|
|
150
|
+
onReject?: (operation: PendingOperation, reason?: any) => void;
|
|
151
|
+
/** Called when an operation is removed/cleaned up */
|
|
152
|
+
onCleanup?: (operation: PendingOperation) => void;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Statistics about pending operations
|
|
156
|
+
*/
|
|
157
|
+
export interface PendingOperationStats {
|
|
158
|
+
/** Total number of pending operations */
|
|
159
|
+
total: number;
|
|
160
|
+
/** Number of operations by type */
|
|
161
|
+
byType: Record<string, number>;
|
|
162
|
+
/** Number of operations that will expire soon (within next minute) */
|
|
163
|
+
expiringSoon: number;
|
|
164
|
+
/** Oldest operation timestamp */
|
|
165
|
+
oldestTimestamp?: number;
|
|
166
|
+
/** Average age of operations in milliseconds */
|
|
167
|
+
averageAge: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Abstract base class for pending operations management
|
|
171
|
+
* Contains all common functionality while allowing specialized subclasses
|
|
172
|
+
*/
|
|
173
|
+
export declare abstract class PendingOperationsBase {
|
|
174
|
+
protected operations: Map<string, PendingOperation>;
|
|
175
|
+
protected config: Required<PendingOperationsConfig>;
|
|
176
|
+
protected cleanupIntervalHandle?: NodeJS.Timeout;
|
|
177
|
+
protected eventHandlers: PendingOperationEventHandlers;
|
|
178
|
+
constructor(config?: PendingOperationsConfig, eventHandlers?: PendingOperationEventHandlers);
|
|
179
|
+
/**
|
|
180
|
+
* Resolve a pending operation with a result
|
|
181
|
+
*/
|
|
182
|
+
resolve(id: string, result?: any): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Reject a pending operation with a reason
|
|
185
|
+
*/
|
|
186
|
+
reject(id: string, reason?: any): boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Get a pending operation by ID
|
|
189
|
+
*/
|
|
190
|
+
get(id: string): PendingOperation | undefined;
|
|
191
|
+
/**
|
|
192
|
+
* Check if an operation exists
|
|
193
|
+
*/
|
|
194
|
+
has(id: string): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Remove an operation without resolving or rejecting
|
|
197
|
+
*/
|
|
198
|
+
remove(id: string): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Get all pending operations
|
|
201
|
+
*/
|
|
202
|
+
getAll(): Map<string, PendingOperation>;
|
|
203
|
+
/**
|
|
204
|
+
* Get pending operations by type
|
|
205
|
+
*/
|
|
206
|
+
getByType(type: PendingOperation["type"]): PendingOperation[];
|
|
207
|
+
/**
|
|
208
|
+
* Clear all pending operations
|
|
209
|
+
*/
|
|
210
|
+
clear(reason?: any): number;
|
|
211
|
+
/**
|
|
212
|
+
* Clean up expired operations
|
|
213
|
+
*/
|
|
214
|
+
cleanupExpired(): number;
|
|
215
|
+
/**
|
|
216
|
+
* Get statistics about pending operations
|
|
217
|
+
*/
|
|
218
|
+
getStats(): PendingOperationStats;
|
|
219
|
+
/**
|
|
220
|
+
* Start automatic cleanup of expired operations
|
|
221
|
+
*/
|
|
222
|
+
private startAutoCleanup;
|
|
223
|
+
/**
|
|
224
|
+
* Stop automatic cleanup
|
|
225
|
+
*/
|
|
226
|
+
stopAutoCleanup(): void;
|
|
227
|
+
/**
|
|
228
|
+
* Handle operation timeout
|
|
229
|
+
*/
|
|
230
|
+
protected handleTimeout({ id }: {
|
|
231
|
+
id: string;
|
|
232
|
+
}): void;
|
|
233
|
+
/**
|
|
234
|
+
* Destroy the pending operations manager
|
|
235
|
+
*/
|
|
236
|
+
destroy(reason?: any): void;
|
|
237
|
+
/**
|
|
238
|
+
* Get the current configuration
|
|
239
|
+
*/
|
|
240
|
+
getConfig(): Required<PendingOperationsConfig>;
|
|
241
|
+
/**
|
|
242
|
+
* Update event handlers
|
|
243
|
+
*/
|
|
244
|
+
setEventHandlers(handlers: PendingOperationEventHandlers): void;
|
|
245
|
+
/**
|
|
246
|
+
* Abstract method that subclasses must implement for their specific operation type
|
|
247
|
+
*/
|
|
248
|
+
abstract add(...args: any[]): any;
|
|
249
|
+
handleAdd(options?: {
|
|
250
|
+
timeout?: number;
|
|
251
|
+
customId?: string;
|
|
252
|
+
}): {
|
|
253
|
+
id: string;
|
|
254
|
+
timeout: number;
|
|
255
|
+
timeoutHandle: NodeJS.Timeout | undefined;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Specialized class for promise-based operations only
|
|
260
|
+
* Enforces type safety by only exposing promise-related methods
|
|
261
|
+
*/
|
|
262
|
+
export declare class PromisePendingOperations extends PendingOperationsBase {
|
|
263
|
+
/**
|
|
264
|
+
* Add a promise-based pending operation
|
|
265
|
+
*/
|
|
266
|
+
add(data: any, options?: {
|
|
267
|
+
timeout?: number;
|
|
268
|
+
customId?: string;
|
|
269
|
+
}): {
|
|
270
|
+
id: string;
|
|
271
|
+
promise: Promise<any>;
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Specialized class for data-based operations only
|
|
276
|
+
* Enforces type safety by only exposing data-related methods
|
|
277
|
+
*/
|
|
278
|
+
export declare class DataPendingOperations extends PendingOperationsBase {
|
|
279
|
+
/**
|
|
280
|
+
* Add a data-based pending operation
|
|
281
|
+
*/
|
|
282
|
+
add(data: any, options?: {
|
|
283
|
+
timeout?: number;
|
|
284
|
+
customId?: string;
|
|
285
|
+
}): {
|
|
286
|
+
id: string;
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Create a PendingOperations instance with Promise-based operations
|
|
291
|
+
* Optimized for server-to-client function calls
|
|
292
|
+
*
|
|
293
|
+
* @param config Optional configuration for the pending operations
|
|
294
|
+
* @param eventHandlers Optional event handlers for operation lifecycle
|
|
295
|
+
* @returns PromisePendingOperations instance that only allows promise operations
|
|
296
|
+
*/
|
|
297
|
+
export declare function createPromisePendingOperations(config?: PendingOperationsConfig, eventHandlers?: PendingOperationEventHandlers): PromisePendingOperations;
|
|
298
|
+
/**
|
|
299
|
+
* Create a PendingOperations instance with Data-based operations
|
|
300
|
+
* Optimized for both request processing and context handling
|
|
301
|
+
*
|
|
302
|
+
* @param config Optional configuration for the pending operations
|
|
303
|
+
* @param eventHandlers Optional event handlers for operation lifecycle
|
|
304
|
+
* @returns DataPendingOperations instance that only allows data operations
|
|
305
|
+
*/
|
|
306
|
+
export declare function createDataPendingOperations(config?: PendingOperationsConfig, eventHandlers?: PendingOperationEventHandlers): DataPendingOperations;
|
|
307
|
+
export declare class JSONRPCCall {
|
|
308
|
+
private pendingRequests;
|
|
309
|
+
constructor(config?: PendingOperationsConfig);
|
|
310
|
+
/**
|
|
311
|
+
* Process a JSON-RPC response
|
|
312
|
+
*/
|
|
313
|
+
handleResponse(response: JSONRPCResponse): void;
|
|
314
|
+
/**
|
|
315
|
+
* Send a JSON-RPC request and return a promise for the response
|
|
316
|
+
*/
|
|
317
|
+
handleRequest(method: string, params?: any, options?: {
|
|
318
|
+
timeout?: number;
|
|
319
|
+
}): Promise<any>;
|
|
320
|
+
/**
|
|
321
|
+
* Get manager statistics
|
|
322
|
+
*/
|
|
323
|
+
getStats(): {
|
|
324
|
+
pendingRequests: PendingOperationStats;
|
|
325
|
+
};
|
|
326
|
+
/**
|
|
327
|
+
* Clean up resources
|
|
328
|
+
*/
|
|
329
|
+
destroy(): void;
|
|
330
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timer Mock Utilities
|
|
3
|
+
*
|
|
4
|
+
* These utilities provide centralized timer mocking for tests that need to control time.
|
|
5
|
+
* This ensures consistent behavior across all tests that use setTimeout, setInterval, etc.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Centralized timer mock manager for controlling time in tests
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Mock setTimeout with virtual clock advancement
|
|
12
|
+
* - Mock Math.random for deterministic jitter elimination
|
|
13
|
+
* - Track virtual time progression
|
|
14
|
+
* - Immediate callback execution for fast tests
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { TimerMockManager } from "../util_tests/TimerMockManager.js";
|
|
19
|
+
*
|
|
20
|
+
* let timerMock: TimerMockManager;
|
|
21
|
+
*
|
|
22
|
+
* beforeEach(() => {
|
|
23
|
+
* timerMock = new TimerMockManager();
|
|
24
|
+
* timerMock.setup();
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* afterEach(() => {
|
|
28
|
+
* timerMock.restore();
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class TimerMockManager {
|
|
33
|
+
private originalSetTimeout?;
|
|
34
|
+
private originalMathRandom?;
|
|
35
|
+
private mockSetTimeout?;
|
|
36
|
+
private virtualClock;
|
|
37
|
+
/**
|
|
38
|
+
* Setup timer mocks
|
|
39
|
+
* @param executeImmediately - Whether to execute callbacks immediately (default: true for fast tests)
|
|
40
|
+
* @param mockRandomValue - Fixed value for Math.random (default: 0 to eliminate jitter)
|
|
41
|
+
*/
|
|
42
|
+
setup(executeImmediately?: boolean, mockRandomValue?: number): void;
|
|
43
|
+
/**
|
|
44
|
+
* Restore original timer functions
|
|
45
|
+
*/
|
|
46
|
+
restore(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Get the current virtual clock time
|
|
49
|
+
*/
|
|
50
|
+
getVirtualClock(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Reset virtual clock to zero
|
|
53
|
+
*/
|
|
54
|
+
resetClock(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Get the mock setTimeout function for assertions
|
|
57
|
+
*/
|
|
58
|
+
getMockSetTimeout(): any;
|
|
59
|
+
/**
|
|
60
|
+
* Verify that setTimeout was called with expected parameters
|
|
61
|
+
*/
|
|
62
|
+
expectSetTimeoutCalled(times: number, delay?: number): void;
|
|
63
|
+
}
|
package/package.json
CHANGED