@agentunion/fastaun 0.5.9 → 0.5.10
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/CHANGELOG.md +32 -0
- package/_packed_docs/CHANGELOG.md +32 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +6 -5
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +43 -8
- package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
- package/dist/agent-md.js +5 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/auth.d.ts +2 -1
- package/dist/auth.js +30 -44
- package/dist/auth.js.map +1 -1
- package/dist/client/delivery.d.ts +21 -5
- package/dist/client/delivery.js +236 -52
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +6 -6
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +5 -14
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -0
- package/dist/client/rpc-pipeline.js +33 -1
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.js +43 -17
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.js +21 -27
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +23 -8
- package/dist/events.js +120 -26
- package/dist/events.js.map +1 -1
- package/dist/register-flow.js +15 -111
- package/dist/register-flow.js.map +1 -1
- package/dist/transport.d.ts +32 -3
- package/dist/transport.js +758 -33
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/transport.js
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import WebSocket from 'ws';
|
|
11
11
|
import * as crypto from 'node:crypto';
|
|
12
|
-
import {
|
|
12
|
+
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
13
|
+
import { AUNError, AuthError, CertificateRevokedError, ClientSignatureError, ConnectionError, E2EEDecryptFailedError, E2EEDegradedError, E2EEError, GroupError, GroupNotFoundError, GroupStateError, IdentityConflictError, NotFoundError, PermissionError, RateLimitError, SerializationError, SessionError, StateError, TimeoutError, ValidationError, VersionConflictError, mapRemoteError, } from './errors.js';
|
|
13
14
|
import { isJsonObject } from './types.js';
|
|
14
15
|
const MAX_WS_PAYLOAD_SIZE = 1_000_000;
|
|
15
16
|
const MAX_RPC_INFLIGHT = 16;
|
|
@@ -236,6 +237,226 @@ const EVENT_NAME_MAP = {
|
|
|
236
237
|
'group.message_recalled': 'group.message_recalled',
|
|
237
238
|
'storage.object_changed': 'storage.object_changed',
|
|
238
239
|
};
|
|
240
|
+
function serializeWorkerError(error) {
|
|
241
|
+
if (error instanceof AUNError) {
|
|
242
|
+
const payload = {
|
|
243
|
+
name: error.name,
|
|
244
|
+
message: error.message,
|
|
245
|
+
code: error.code,
|
|
246
|
+
stringCode: error.stringCode,
|
|
247
|
+
data: error.data,
|
|
248
|
+
retryable: error.retryable,
|
|
249
|
+
traceId: error.traceId,
|
|
250
|
+
};
|
|
251
|
+
if (error instanceof E2EEError) {
|
|
252
|
+
payload.localCode = error.localCode;
|
|
253
|
+
payload.closeReason = error.closeReason;
|
|
254
|
+
}
|
|
255
|
+
return payload;
|
|
256
|
+
}
|
|
257
|
+
if (error instanceof Error) {
|
|
258
|
+
return { name: error.name, message: error.message };
|
|
259
|
+
}
|
|
260
|
+
return { name: 'Error', message: String(error) };
|
|
261
|
+
}
|
|
262
|
+
function deserializeWorkerError(payload) {
|
|
263
|
+
const message = String(payload?.message ?? 'transport worker error');
|
|
264
|
+
const options = {
|
|
265
|
+
code: payload?.code,
|
|
266
|
+
stringCode: payload?.stringCode,
|
|
267
|
+
data: payload?.data ?? null,
|
|
268
|
+
retryable: payload?.retryable,
|
|
269
|
+
traceId: payload?.traceId,
|
|
270
|
+
};
|
|
271
|
+
switch (payload?.name) {
|
|
272
|
+
case 'ConnectionError': return new ConnectionError(message, options);
|
|
273
|
+
case 'TimeoutError': return new TimeoutError(message, options);
|
|
274
|
+
case 'AuthError': return new AuthError(message, options);
|
|
275
|
+
case 'PermissionError': return new PermissionError(message, options);
|
|
276
|
+
case 'ValidationError': return new ValidationError(message, options);
|
|
277
|
+
case 'NotFoundError': return new NotFoundError(message, options);
|
|
278
|
+
case 'RateLimitError': return new RateLimitError(message, options);
|
|
279
|
+
case 'StateError': return new StateError(message, options);
|
|
280
|
+
case 'SerializationError': return new SerializationError(message, options);
|
|
281
|
+
case 'SessionError': return new SessionError(message, options);
|
|
282
|
+
case 'VersionConflictError': return new VersionConflictError(message, options);
|
|
283
|
+
case 'GroupError': return new GroupError(message, options);
|
|
284
|
+
case 'GroupNotFoundError': return new GroupNotFoundError(message, options);
|
|
285
|
+
case 'GroupStateError': return new GroupStateError(message, options);
|
|
286
|
+
case 'E2EEDecryptFailedError': return new E2EEDecryptFailedError(message, options);
|
|
287
|
+
case 'E2EEDegradedError': return new E2EEDegradedError(message, options);
|
|
288
|
+
case 'E2EEError': return new E2EEError(message, {
|
|
289
|
+
...options,
|
|
290
|
+
localCode: payload?.localCode,
|
|
291
|
+
closeReason: payload?.closeReason,
|
|
292
|
+
});
|
|
293
|
+
case 'CertificateRevokedError': return new CertificateRevokedError(message, options);
|
|
294
|
+
case 'IdentityConflictError': return new IdentityConflictError(message, options);
|
|
295
|
+
case 'ClientSignatureError': return new ClientSignatureError(message, options);
|
|
296
|
+
case 'AUNError': return new AUNError(message, options);
|
|
297
|
+
case 'TypeError': return new TypeError(message);
|
|
298
|
+
case 'Error': return new Error(message);
|
|
299
|
+
default:
|
|
300
|
+
if (payload?.code !== undefined) {
|
|
301
|
+
return mapRemoteError({
|
|
302
|
+
code: payload.code,
|
|
303
|
+
message,
|
|
304
|
+
data: payload.data ?? null,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return new Error(message);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function executeShortRpcEnvelope(url, method, params, signal) {
|
|
311
|
+
return new Promise((resolve, reject) => {
|
|
312
|
+
if (signal.aborted) {
|
|
313
|
+
reject(new AuthError('short RPC cancelled'));
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const requestId = `pre-${method}`;
|
|
317
|
+
const payload = JSON.stringify({ jsonrpc: '2.0', id: requestId, method, params });
|
|
318
|
+
const ws = new WebSocket(url, { handshakeTimeout: 5_000, rejectUnauthorized: false });
|
|
319
|
+
let opened = false;
|
|
320
|
+
let challengeReceived = false;
|
|
321
|
+
let settled = false;
|
|
322
|
+
let timer = null;
|
|
323
|
+
const cleanup = () => {
|
|
324
|
+
if (timer !== null)
|
|
325
|
+
clearTimeout(timer);
|
|
326
|
+
signal.removeEventListener('abort', onAbort);
|
|
327
|
+
ws.off('open', onOpen);
|
|
328
|
+
ws.off('message', onMessage);
|
|
329
|
+
ws.off('error', onError);
|
|
330
|
+
ws.off('close', onClose);
|
|
331
|
+
};
|
|
332
|
+
const finish = (callback) => {
|
|
333
|
+
if (settled)
|
|
334
|
+
return;
|
|
335
|
+
settled = true;
|
|
336
|
+
cleanup();
|
|
337
|
+
try {
|
|
338
|
+
ws.close();
|
|
339
|
+
}
|
|
340
|
+
catch { /* ignore */ }
|
|
341
|
+
callback();
|
|
342
|
+
};
|
|
343
|
+
const fail = (error) => finish(() => reject(error));
|
|
344
|
+
const onOpen = () => {
|
|
345
|
+
opened = true;
|
|
346
|
+
timer = setTimeout(() => fail(new AuthError(`shortRpc timeout: ${method}`)), 15_000);
|
|
347
|
+
};
|
|
348
|
+
const onAbort = () => {
|
|
349
|
+
ws.once('error', () => { });
|
|
350
|
+
try {
|
|
351
|
+
ws.terminate();
|
|
352
|
+
}
|
|
353
|
+
catch { /* ignore */ }
|
|
354
|
+
fail(new AuthError('short RPC cancelled'));
|
|
355
|
+
};
|
|
356
|
+
const onError = (error) => fail(new AuthError(opened ? `websocket error: ${error.message}` : `websocket connect failed: ${error.message}`));
|
|
357
|
+
const onClose = () => fail(new AuthError(challengeReceived
|
|
358
|
+
? `websocket closed before ${method} response`
|
|
359
|
+
: 'websocket closed before challenge'));
|
|
360
|
+
const onMessage = (data) => {
|
|
361
|
+
try {
|
|
362
|
+
const raw = Buffer.isBuffer(data)
|
|
363
|
+
? data.toString('utf-8')
|
|
364
|
+
: data instanceof ArrayBuffer
|
|
365
|
+
? Buffer.from(data).toString('utf-8')
|
|
366
|
+
: String(data);
|
|
367
|
+
const message = JSON.parse(raw);
|
|
368
|
+
if (!isJsonObject(message)) {
|
|
369
|
+
fail(new ValidationError(`invalid WebSocket frame before ${method}`));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (!challengeReceived) {
|
|
373
|
+
if (message.method !== 'challenge')
|
|
374
|
+
return;
|
|
375
|
+
challengeReceived = true;
|
|
376
|
+
ws.send(payload);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (message.id !== requestId)
|
|
380
|
+
return;
|
|
381
|
+
finish(() => resolve(message));
|
|
382
|
+
}
|
|
383
|
+
catch (error) {
|
|
384
|
+
fail(error instanceof Error ? error : new AuthError(String(error)));
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
ws.on('open', onOpen);
|
|
388
|
+
ws.on('message', onMessage);
|
|
389
|
+
ws.on('error', onError);
|
|
390
|
+
ws.on('close', onClose);
|
|
391
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
392
|
+
if (signal.aborted)
|
|
393
|
+
onAbort();
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
export function shortRpcViaNetworkActor(url, method, params) {
|
|
397
|
+
if (!isMainThread || process.env.VITEST || process.env.AUN_TRANSPORT_LOCAL === '1') {
|
|
398
|
+
const controller = new AbortController();
|
|
399
|
+
return Object.assign(executeShortRpcEnvelope(url, method, params, controller.signal), {
|
|
400
|
+
cancel: () => controller.abort(),
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
const id = `short-rpc-${crypto.randomUUID()}`;
|
|
404
|
+
let worker = null;
|
|
405
|
+
let settled = false;
|
|
406
|
+
let resolveResult;
|
|
407
|
+
let rejectResult;
|
|
408
|
+
const promise = new Promise((resolve, reject) => {
|
|
409
|
+
resolveResult = resolve;
|
|
410
|
+
rejectResult = reject;
|
|
411
|
+
});
|
|
412
|
+
const cleanup = () => {
|
|
413
|
+
const current = worker;
|
|
414
|
+
worker = null;
|
|
415
|
+
if (!current)
|
|
416
|
+
return;
|
|
417
|
+
current.removeAllListeners();
|
|
418
|
+
void current.terminate().catch(() => { });
|
|
419
|
+
};
|
|
420
|
+
const resolveOnce = (value) => {
|
|
421
|
+
if (settled)
|
|
422
|
+
return;
|
|
423
|
+
settled = true;
|
|
424
|
+
cleanup();
|
|
425
|
+
resolveResult(value);
|
|
426
|
+
};
|
|
427
|
+
const rejectOnce = (error) => {
|
|
428
|
+
if (settled)
|
|
429
|
+
return;
|
|
430
|
+
settled = true;
|
|
431
|
+
cleanup();
|
|
432
|
+
rejectResult(error);
|
|
433
|
+
};
|
|
434
|
+
try {
|
|
435
|
+
worker = new Worker(new URL(import.meta.url), {
|
|
436
|
+
execArgv: process.execArgv.filter((arg) => !arg.startsWith('--input-type')),
|
|
437
|
+
workerData: { aunShortRpcWorker: true, id, url, method, params },
|
|
438
|
+
});
|
|
439
|
+
worker.on('message', (message) => {
|
|
440
|
+
if (message.type !== 'reply' || message.id !== id)
|
|
441
|
+
return;
|
|
442
|
+
if (message.ok && isJsonObject(message.result))
|
|
443
|
+
resolveOnce(message.result);
|
|
444
|
+
else
|
|
445
|
+
rejectOnce(deserializeWorkerError(message.error));
|
|
446
|
+
});
|
|
447
|
+
worker.on('error', (error) => rejectOnce(new AuthError(`short RPC worker error: ${error.message}`)));
|
|
448
|
+
worker.on('exit', (code) => {
|
|
449
|
+
if (!settled)
|
|
450
|
+
rejectOnce(new AuthError(`short RPC worker exited: ${code}`));
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
rejectOnce(new AuthError(`short RPC worker unavailable: ${error instanceof Error ? error.message : String(error)}`));
|
|
455
|
+
}
|
|
456
|
+
return Object.assign(promise, {
|
|
457
|
+
cancel: () => rejectOnce(new AuthError('short RPC cancelled')),
|
|
458
|
+
});
|
|
459
|
+
}
|
|
239
460
|
/**
|
|
240
461
|
* WebSocket JSON-RPC 2.0 传输层
|
|
241
462
|
*/
|
|
@@ -273,6 +494,17 @@ export class RPCTransport {
|
|
|
273
494
|
_traceMode = 'off';
|
|
274
495
|
// Trace observer:observer(traceInfo) 在每次 RPC/事件携带 _trace 时调用
|
|
275
496
|
_traceObserver = null;
|
|
497
|
+
// Network actor command bridge: transport state transitions are started in FIFO order.
|
|
498
|
+
_actorTail = Promise.resolve();
|
|
499
|
+
_actorBusy = false;
|
|
500
|
+
_worker = null;
|
|
501
|
+
_workerPending = new Map();
|
|
502
|
+
_workerSeq = 0;
|
|
503
|
+
_workerConnected = false;
|
|
504
|
+
_workerGeneration = null;
|
|
505
|
+
_workerConnecting = false;
|
|
506
|
+
_workerBufferedEvents = [];
|
|
507
|
+
_workerOptions;
|
|
276
508
|
constructor(opts) {
|
|
277
509
|
this._logger = opts.logger ?? _noopLogger;
|
|
278
510
|
this._dispatcher = opts.eventDispatcher;
|
|
@@ -281,17 +513,262 @@ export class RPCTransport {
|
|
|
281
513
|
this._onDisconnect = opts.onDisconnect ?? null;
|
|
282
514
|
this._verifySsl = opts.verifySsl ?? true;
|
|
283
515
|
this._dnsNet = opts.dnsNet ?? null;
|
|
516
|
+
this._workerOptions = { timeout: opts.timeout, verifySsl: opts.verifySsl };
|
|
517
|
+
this._dispatcher.subscribe('_transport.observer', (item) => this._dispatchObserver(item));
|
|
518
|
+
// 断线回调统一经过应用分发边界,避免 WebSocket close 处理器直接进入用户/客户端逻辑。
|
|
519
|
+
this._dispatcher.subscribe('_transport.disconnect', (item) => {
|
|
520
|
+
const event = item;
|
|
521
|
+
const callback = this._onDisconnect;
|
|
522
|
+
if (!callback || !event)
|
|
523
|
+
return;
|
|
524
|
+
try {
|
|
525
|
+
const result = callback(event.error ?? null, event.closeCode);
|
|
526
|
+
if (result && typeof result.then === 'function') {
|
|
527
|
+
return Promise.resolve(result).catch(() => {
|
|
528
|
+
this._logger.warn('disconnect callback error');
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch {
|
|
533
|
+
this._logger.warn('disconnect callback error');
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
if (isMainThread && !process.env.VITEST && process.env.AUN_TRANSPORT_LOCAL !== '1') {
|
|
537
|
+
this._startNetworkWorker(opts);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
_startNetworkWorker(opts) {
|
|
541
|
+
try {
|
|
542
|
+
const worker = new Worker(new URL(import.meta.url), {
|
|
543
|
+
execArgv: process.execArgv.filter((arg) => !arg.startsWith('--input-type')),
|
|
544
|
+
workerData: {
|
|
545
|
+
aunTransportWorker: true,
|
|
546
|
+
timeout: opts.timeout ?? 10_000,
|
|
547
|
+
verifySsl: opts.verifySsl ?? true,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
this._worker = worker;
|
|
551
|
+
worker.on('message', (message) => {
|
|
552
|
+
if (this._worker !== worker)
|
|
553
|
+
return;
|
|
554
|
+
if (message.type === 'event') {
|
|
555
|
+
this._handleWorkerEvent(message);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (!message.id)
|
|
559
|
+
return;
|
|
560
|
+
const pending = this._workerPending.get(message.id);
|
|
561
|
+
if (!pending)
|
|
562
|
+
return;
|
|
563
|
+
if (typeof message.generation === 'number'
|
|
564
|
+
&& this._workerGeneration !== null
|
|
565
|
+
&& pending.commandType !== 'connect'
|
|
566
|
+
&& pending.commandType !== 'close'
|
|
567
|
+
&& message.generation !== this._workerGeneration) {
|
|
568
|
+
this._workerPending.delete(message.id);
|
|
569
|
+
if (pending.timer !== null)
|
|
570
|
+
clearTimeout(pending.timer);
|
|
571
|
+
pending.reject(new ConnectionError('stale transport worker response'));
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
this._workerPending.delete(message.id);
|
|
575
|
+
if (pending.timer !== null)
|
|
576
|
+
clearTimeout(pending.timer);
|
|
577
|
+
if (pending.commandType === 'connect' && message.ok && typeof message.generation === 'number') {
|
|
578
|
+
this._workerGeneration = message.generation;
|
|
579
|
+
}
|
|
580
|
+
if (message.ok)
|
|
581
|
+
pending.resolve(message.result);
|
|
582
|
+
else {
|
|
583
|
+
if (pending.commandType === 'connect') {
|
|
584
|
+
this._workerGeneration = null;
|
|
585
|
+
this._workerBufferedEvents = [];
|
|
586
|
+
}
|
|
587
|
+
pending.reject(deserializeWorkerError(message.error));
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
worker.on('error', (error) => {
|
|
591
|
+
if (this._worker !== worker)
|
|
592
|
+
return;
|
|
593
|
+
this._worker = null;
|
|
594
|
+
this._workerConnected = false;
|
|
595
|
+
this._workerConnecting = false;
|
|
596
|
+
this._workerGeneration = null;
|
|
597
|
+
this._workerBufferedEvents = [];
|
|
598
|
+
for (const pending of this._workerPending.values()) {
|
|
599
|
+
if (pending.timer !== null)
|
|
600
|
+
clearTimeout(pending.timer);
|
|
601
|
+
pending.reject(error);
|
|
602
|
+
}
|
|
603
|
+
this._workerPending.clear();
|
|
604
|
+
this._dispatcher.enqueue('connection.error', { error });
|
|
605
|
+
});
|
|
606
|
+
worker.on('exit', (code) => {
|
|
607
|
+
if (this._worker !== worker)
|
|
608
|
+
return;
|
|
609
|
+
this._worker = null;
|
|
610
|
+
this._workerConnected = false;
|
|
611
|
+
this._workerConnecting = false;
|
|
612
|
+
this._workerGeneration = null;
|
|
613
|
+
this._workerBufferedEvents = [];
|
|
614
|
+
const error = new ConnectionError(`transport worker exited: ${code}`);
|
|
615
|
+
for (const pending of this._workerPending.values()) {
|
|
616
|
+
if (pending.timer !== null)
|
|
617
|
+
clearTimeout(pending.timer);
|
|
618
|
+
pending.reject(error);
|
|
619
|
+
}
|
|
620
|
+
this._workerPending.clear();
|
|
621
|
+
if (code !== 0)
|
|
622
|
+
this._dispatcher.enqueue('connection.error', { error: new Error(`transport worker exited: ${code}`) });
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
catch (error) {
|
|
626
|
+
this._worker = null;
|
|
627
|
+
this._logger.warn(`transport worker unavailable; using local transport: ${error instanceof Error ? error.message : String(error)}`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
_ensureNetworkWorker() {
|
|
631
|
+
if (this._worker || !isMainThread || process.env.VITEST || process.env.AUN_TRANSPORT_LOCAL === '1')
|
|
632
|
+
return;
|
|
633
|
+
this._startNetworkWorker(this._workerOptions);
|
|
634
|
+
const worker = this._worker;
|
|
635
|
+
if (!worker)
|
|
636
|
+
return;
|
|
637
|
+
worker.postMessage({ type: 'set_timeout', value: this._timeout });
|
|
638
|
+
worker.postMessage({ type: 'set_connect_timeout', value: this._connectTimeout });
|
|
639
|
+
worker.postMessage({ type: 'set_trace_mode', value: this._traceMode });
|
|
640
|
+
}
|
|
641
|
+
_dnsFallbackSnapshot(url) {
|
|
642
|
+
if (!this._dnsNet)
|
|
643
|
+
return null;
|
|
644
|
+
try {
|
|
645
|
+
const hostname = new URL(url).hostname;
|
|
646
|
+
const cached = this._dnsNet.loadDnsCache(hostname);
|
|
647
|
+
return cached ? { hostname, ip: cached.ip, port: cached.port } : null;
|
|
648
|
+
}
|
|
649
|
+
catch {
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
_handleWorkerEvent(message) {
|
|
654
|
+
if (typeof message.generation !== 'number')
|
|
655
|
+
return;
|
|
656
|
+
if (this._workerConnecting) {
|
|
657
|
+
this._workerBufferedEvents.push(message);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (this._workerGeneration === null || message.generation !== this._workerGeneration)
|
|
661
|
+
return;
|
|
662
|
+
if (message.event === '_transport.observer') {
|
|
663
|
+
const item = message.payload;
|
|
664
|
+
const observer = item?.kind?.includes('trace') ? this._traceObserver : this._metaObserver;
|
|
665
|
+
if (observer && item?.payload)
|
|
666
|
+
this._dispatcher.enqueue('_transport.observer', {
|
|
667
|
+
kind: item.kind ?? 'observer', observer, payload: item.payload,
|
|
668
|
+
});
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (message.event === '_transport.disconnect') {
|
|
672
|
+
this._workerConnected = false;
|
|
673
|
+
this._workerGeneration = null;
|
|
674
|
+
const payload = (message.payload && typeof message.payload === 'object')
|
|
675
|
+
? { ...message.payload }
|
|
676
|
+
: {};
|
|
677
|
+
const workerError = payload.error;
|
|
678
|
+
if (workerError && typeof workerError === 'object') {
|
|
679
|
+
payload.error = new ConnectionError(String(workerError.message ?? 'transport disconnected'));
|
|
680
|
+
}
|
|
681
|
+
this._dispatcher.enqueue('_transport.disconnect', payload);
|
|
682
|
+
}
|
|
683
|
+
else if (message.event) {
|
|
684
|
+
this._dispatcher.enqueue(message.event, message.payload);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
_flushWorkerEvents() {
|
|
688
|
+
const buffered = this._workerBufferedEvents;
|
|
689
|
+
this._workerBufferedEvents = [];
|
|
690
|
+
for (const message of buffered)
|
|
691
|
+
this._handleWorkerEvent(message);
|
|
692
|
+
}
|
|
693
|
+
_workerCommand(command) {
|
|
694
|
+
this._ensureNetworkWorker();
|
|
695
|
+
if (!this._worker)
|
|
696
|
+
return Promise.reject(new ConnectionError('transport worker unavailable'));
|
|
697
|
+
const worker = this._worker;
|
|
698
|
+
const id = `worker-${++this._workerSeq}`;
|
|
699
|
+
const timeoutMs = command.type === 'call'
|
|
700
|
+
? null
|
|
701
|
+
: command.type === 'connect' ? this._connectTimeout + 1_000 : 30_000;
|
|
702
|
+
let posted = false;
|
|
703
|
+
const promise = new Promise((resolve, reject) => {
|
|
704
|
+
const timer = timeoutMs === null ? null : setTimeout(() => {
|
|
705
|
+
this._workerPending.delete(id);
|
|
706
|
+
if (posted) {
|
|
707
|
+
try {
|
|
708
|
+
worker.postMessage({ type: 'cancel', targetId: id });
|
|
709
|
+
}
|
|
710
|
+
catch { /* worker exit path */ }
|
|
711
|
+
}
|
|
712
|
+
reject(new TimeoutError(`rpc timeout: ${String(command.method ?? command.type)}`, { retryable: true }));
|
|
713
|
+
}, timeoutMs);
|
|
714
|
+
this._workerPending.set(id, { resolve: resolve, reject, timer, commandType: command.type });
|
|
715
|
+
const postCommand = () => {
|
|
716
|
+
if (!this._workerPending.has(id))
|
|
717
|
+
return;
|
|
718
|
+
try {
|
|
719
|
+
worker.postMessage({ ...command, id });
|
|
720
|
+
posted = true;
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
this._workerPending.delete(id);
|
|
724
|
+
if (timer !== null)
|
|
725
|
+
clearTimeout(timer);
|
|
726
|
+
reject(error);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
if (command.type === 'call')
|
|
730
|
+
queueMicrotask(postCommand);
|
|
731
|
+
else
|
|
732
|
+
postCommand();
|
|
733
|
+
});
|
|
734
|
+
return Object.assign(promise, {
|
|
735
|
+
cancel: () => {
|
|
736
|
+
const pending = this._workerPending.get(id);
|
|
737
|
+
if (!pending)
|
|
738
|
+
return;
|
|
739
|
+
this._workerPending.delete(id);
|
|
740
|
+
if (pending.timer !== null)
|
|
741
|
+
clearTimeout(pending.timer);
|
|
742
|
+
if (posted) {
|
|
743
|
+
try {
|
|
744
|
+
worker.postMessage({ type: 'cancel', targetId: id });
|
|
745
|
+
}
|
|
746
|
+
catch { /* worker exit path */ }
|
|
747
|
+
}
|
|
748
|
+
pending.reject(new TimeoutError(`rpc cancelled: ${String(command.method ?? command.type)}`, { retryable: true }));
|
|
749
|
+
},
|
|
750
|
+
});
|
|
284
751
|
}
|
|
285
752
|
/** 设置默认 RPC 超时(毫秒) */
|
|
286
753
|
setTimeout(timeout) {
|
|
287
754
|
this._timeout = timeout;
|
|
755
|
+
if (this._worker) {
|
|
756
|
+
this._worker.postMessage({ type: 'set_timeout', value: timeout });
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
288
759
|
}
|
|
289
760
|
/** 设置 WebSocket 建连及 challenge 超时(毫秒)。 */
|
|
290
761
|
setConnectTimeout(timeout) {
|
|
291
762
|
this._connectTimeout = timeout;
|
|
763
|
+
if (this._worker) {
|
|
764
|
+
this._worker.postMessage({ type: 'set_connect_timeout', value: timeout });
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
292
767
|
}
|
|
293
768
|
/** 当前是否仍有可用的 WebSocket。 */
|
|
294
769
|
isConnected() {
|
|
770
|
+
if (this._worker)
|
|
771
|
+
return this._workerConnected;
|
|
295
772
|
return !this._closed && this._ws !== null && this._ws.readyState === WebSocket.OPEN;
|
|
296
773
|
}
|
|
297
774
|
/**
|
|
@@ -309,11 +786,33 @@ export class RPCTransport {
|
|
|
309
786
|
throw new ValidationError(`invalid trace mode: ${mode}, must be off/log/diag`);
|
|
310
787
|
}
|
|
311
788
|
this._traceMode = mode;
|
|
789
|
+
if (this._worker) {
|
|
790
|
+
this._worker.postMessage({ type: 'set_trace_mode', value: mode });
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
312
793
|
}
|
|
313
794
|
/** 注册 trace observer;observer(traceInfo) 在每次 RPC/事件携带 _trace 时调用。 */
|
|
314
795
|
setTraceObserver(observer) {
|
|
315
796
|
this._traceObserver = observer;
|
|
316
797
|
}
|
|
798
|
+
_queueObserver(kind, observer, payload) {
|
|
799
|
+
this._dispatcher.enqueue('_transport.observer', {
|
|
800
|
+
kind,
|
|
801
|
+
observer,
|
|
802
|
+
payload,
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
async _dispatchObserver(item) {
|
|
806
|
+
const observerEvent = item;
|
|
807
|
+
if (!observerEvent || typeof observerEvent.observer !== 'function')
|
|
808
|
+
return;
|
|
809
|
+
try {
|
|
810
|
+
await observerEvent.observer(observerEvent.payload);
|
|
811
|
+
}
|
|
812
|
+
catch (err) {
|
|
813
|
+
this._logger.debug(`${observerEvent.kind || 'observer'} raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
317
816
|
/** 获取上次连接的 challenge 消息 */
|
|
318
817
|
get challenge() {
|
|
319
818
|
return this._challenge;
|
|
@@ -334,11 +833,99 @@ export class RPCTransport {
|
|
|
334
833
|
release();
|
|
335
834
|
}
|
|
336
835
|
}
|
|
836
|
+
_enqueueActor(operation) {
|
|
837
|
+
this._actorBusy = true;
|
|
838
|
+
const run = this._actorTail.then(async () => {
|
|
839
|
+
return await operation();
|
|
840
|
+
}, async () => {
|
|
841
|
+
return await operation();
|
|
842
|
+
});
|
|
843
|
+
const tail = run.then(() => undefined, () => undefined);
|
|
844
|
+
this._actorTail = tail;
|
|
845
|
+
void tail.then(() => {
|
|
846
|
+
if (this._actorTail === tail)
|
|
847
|
+
this._actorBusy = false;
|
|
848
|
+
});
|
|
849
|
+
return run;
|
|
850
|
+
}
|
|
851
|
+
/** 启动长生命周期 RPC,但不把 actor 队列占用到响应返回。 */
|
|
852
|
+
_enqueueActorStart(operation, cancellationError) {
|
|
853
|
+
let resolveResult;
|
|
854
|
+
let rejectResult;
|
|
855
|
+
const result = new Promise((resolve, reject) => {
|
|
856
|
+
resolveResult = resolve;
|
|
857
|
+
rejectResult = reject;
|
|
858
|
+
});
|
|
859
|
+
let cancel;
|
|
860
|
+
let cancelRequested = false;
|
|
861
|
+
const launch = () => {
|
|
862
|
+
if (cancelRequested)
|
|
863
|
+
return;
|
|
864
|
+
try {
|
|
865
|
+
const inner = operation();
|
|
866
|
+
cancel = inner.cancel;
|
|
867
|
+
inner.then(resolveResult, rejectResult);
|
|
868
|
+
}
|
|
869
|
+
catch (err) {
|
|
870
|
+
rejectResult(err);
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
// 调用命令本身在当前 turn 立即启动(若有 close 等独占命令则排队)。
|
|
874
|
+
// 网络响应仍异步完成,避免改变既有 call() 的入队/并发语义。
|
|
875
|
+
if (this._actorBusy) {
|
|
876
|
+
const gate = this._actorTail.then(launch, launch);
|
|
877
|
+
const tail = gate.then(() => undefined, () => undefined);
|
|
878
|
+
this._actorTail = tail;
|
|
879
|
+
void tail.then(() => {
|
|
880
|
+
if (this._actorTail === tail)
|
|
881
|
+
this._actorBusy = false;
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
launch();
|
|
886
|
+
}
|
|
887
|
+
return Object.assign(result, {
|
|
888
|
+
cancel: () => {
|
|
889
|
+
if (cancelRequested)
|
|
890
|
+
return;
|
|
891
|
+
cancelRequested = true;
|
|
892
|
+
if (cancel) {
|
|
893
|
+
cancel();
|
|
894
|
+
}
|
|
895
|
+
else {
|
|
896
|
+
rejectResult(cancellationError?.() ?? new TimeoutError('rpc cancelled', { retryable: true }));
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
});
|
|
900
|
+
}
|
|
337
901
|
/**
|
|
338
902
|
* 连接到 Gateway WebSocket 端点。
|
|
339
903
|
* 返回初始 challenge 消息(如果有)。
|
|
340
904
|
*/
|
|
341
905
|
async connect(url) {
|
|
906
|
+
this._ensureNetworkWorker();
|
|
907
|
+
if (this._worker) {
|
|
908
|
+
this._workerConnecting = true;
|
|
909
|
+
try {
|
|
910
|
+
const result = await this._workerCommand({
|
|
911
|
+
type: 'connect', url, dnsFallback: this._dnsFallbackSnapshot(url),
|
|
912
|
+
});
|
|
913
|
+
this._workerConnected = true;
|
|
914
|
+
this._workerConnecting = false;
|
|
915
|
+
this._flushWorkerEvents();
|
|
916
|
+
return result;
|
|
917
|
+
}
|
|
918
|
+
catch (error) {
|
|
919
|
+
this._workerConnecting = false;
|
|
920
|
+
this._workerConnected = false;
|
|
921
|
+
this._workerGeneration = null;
|
|
922
|
+
this._workerBufferedEvents = [];
|
|
923
|
+
throw error;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return this._enqueueActorStart(() => this._connectLocal(url));
|
|
927
|
+
}
|
|
928
|
+
async _connectLocal(url) {
|
|
342
929
|
const tStart = Date.now();
|
|
343
930
|
this._logger.debug(`connect enter: url=${url}`);
|
|
344
931
|
// 只串行化清理和监听器安装;后续 connect 可立即取消本次握手。
|
|
@@ -568,6 +1155,31 @@ export class RPCTransport {
|
|
|
568
1155
|
}
|
|
569
1156
|
/** 关闭连接 */
|
|
570
1157
|
async close() {
|
|
1158
|
+
if (this._worker) {
|
|
1159
|
+
const worker = this._worker;
|
|
1160
|
+
try {
|
|
1161
|
+
await this._workerCommand({ type: 'close' });
|
|
1162
|
+
}
|
|
1163
|
+
finally {
|
|
1164
|
+
if (this._worker === worker)
|
|
1165
|
+
this._worker = null;
|
|
1166
|
+
this._workerConnected = false;
|
|
1167
|
+
this._workerConnecting = false;
|
|
1168
|
+
this._workerGeneration = null;
|
|
1169
|
+
this._workerBufferedEvents = [];
|
|
1170
|
+
for (const pending of this._workerPending.values()) {
|
|
1171
|
+
if (pending.timer !== null)
|
|
1172
|
+
clearTimeout(pending.timer);
|
|
1173
|
+
pending.reject(new ConnectionError('transport closed'));
|
|
1174
|
+
}
|
|
1175
|
+
this._workerPending.clear();
|
|
1176
|
+
await worker.terminate();
|
|
1177
|
+
}
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
await this._enqueueActor(() => this._closeLocal());
|
|
1181
|
+
}
|
|
1182
|
+
async _closeLocal() {
|
|
571
1183
|
await this._withConnectionSetup(() => this._closeUnlocked());
|
|
572
1184
|
}
|
|
573
1185
|
/** 已持有连接建立串行权时关闭当前 WebSocket。 */
|
|
@@ -652,7 +1264,13 @@ export class RPCTransport {
|
|
|
652
1264
|
/**
|
|
653
1265
|
* 发送 JSON-RPC 2.0 请求并等待响应。
|
|
654
1266
|
*/
|
|
655
|
-
|
|
1267
|
+
call(method, params, timeout, trace, background = false) {
|
|
1268
|
+
if (this._worker) {
|
|
1269
|
+
return this._workerCommand({ type: 'call', method, params, timeout, trace, background });
|
|
1270
|
+
}
|
|
1271
|
+
return this._enqueueActorStart(() => this._callLocal(method, params, timeout, trace, background), () => new TimeoutError(`rpc cancelled: ${method}`, { retryable: true }));
|
|
1272
|
+
}
|
|
1273
|
+
_callLocal(method, params, timeout, trace, background = false) {
|
|
656
1274
|
if (this._closed || !this._ws) {
|
|
657
1275
|
const suffix = this._lastCloseCode !== null ? `: close code ${this._lastCloseCode}` : '';
|
|
658
1276
|
throw new ConnectionError(`transport not connected${suffix}`, {
|
|
@@ -721,12 +1339,7 @@ export class RPCTransport {
|
|
|
721
1339
|
if (this._metaObserver !== null) {
|
|
722
1340
|
const meta = response._meta;
|
|
723
1341
|
if (meta !== null && typeof meta === 'object' && !Array.isArray(meta)) {
|
|
724
|
-
|
|
725
|
-
this._metaObserver(meta);
|
|
726
|
-
}
|
|
727
|
-
catch (err) {
|
|
728
|
-
this._logger.debug(`meta_observer raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
729
|
-
}
|
|
1342
|
+
this._queueObserver('meta_observer', this._metaObserver, meta);
|
|
730
1343
|
}
|
|
731
1344
|
}
|
|
732
1345
|
// 处理 success 路径的 _trace
|
|
@@ -779,6 +1392,13 @@ export class RPCTransport {
|
|
|
779
1392
|
}
|
|
780
1393
|
/** 发送 JSON-RPC 2.0 Notification,不分配 id,也不等待响应。 */
|
|
781
1394
|
async notify(method, params) {
|
|
1395
|
+
if (this._worker) {
|
|
1396
|
+
await this._workerCommand({ type: 'notify', method, params });
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
return this._enqueueActorStart(() => this._notifyLocal(method, params));
|
|
1400
|
+
}
|
|
1401
|
+
async _notifyLocal(method, params) {
|
|
782
1402
|
if (this._closed || !this._ws) {
|
|
783
1403
|
const suffix = this._lastCloseCode !== null ? `: close code ${this._lastCloseCode}` : '';
|
|
784
1404
|
throw new ConnectionError(`transport not connected${suffix}`, {
|
|
@@ -965,7 +1585,9 @@ export class RPCTransport {
|
|
|
965
1585
|
const enriched = { ...respTrace, spans };
|
|
966
1586
|
this._logger.info(traceDisplay(method, status, elapsedMs, respTrace, spans));
|
|
967
1587
|
if (this._traceObserver !== null) {
|
|
968
|
-
this.
|
|
1588
|
+
this._queueObserver('trace_observer', this._traceObserver, {
|
|
1589
|
+
type: 'rpc', method, trace: enriched, status, duration_ms: elapsedMs,
|
|
1590
|
+
});
|
|
969
1591
|
}
|
|
970
1592
|
}
|
|
971
1593
|
catch (err) {
|
|
@@ -985,7 +1607,7 @@ export class RPCTransport {
|
|
|
985
1607
|
}
|
|
986
1608
|
catch (err) {
|
|
987
1609
|
// 解析异常,发布错误事件
|
|
988
|
-
this._dispatcher.
|
|
1610
|
+
this._dispatcher.enqueue('connection.error', {
|
|
989
1611
|
error: err instanceof Error ? err : String(err),
|
|
990
1612
|
});
|
|
991
1613
|
}
|
|
@@ -1018,8 +1640,10 @@ export class RPCTransport {
|
|
|
1018
1640
|
}
|
|
1019
1641
|
this._backgroundRpcQueue = [];
|
|
1020
1642
|
if (!wasClosed && this._onDisconnect) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1643
|
+
this._dispatcher.enqueue('_transport.disconnect', {
|
|
1644
|
+
error: null,
|
|
1645
|
+
closeCode: code,
|
|
1646
|
+
});
|
|
1023
1647
|
}
|
|
1024
1648
|
});
|
|
1025
1649
|
ws.on('error', (err) => {
|
|
@@ -1027,7 +1651,7 @@ export class RPCTransport {
|
|
|
1027
1651
|
return;
|
|
1028
1652
|
if (!this._closed) {
|
|
1029
1653
|
this._logger.error(`WebSocket error: ${err.message}`);
|
|
1030
|
-
this._dispatcher.
|
|
1654
|
+
this._dispatcher.enqueue('connection.error', { error: err });
|
|
1031
1655
|
}
|
|
1032
1656
|
});
|
|
1033
1657
|
this._startWebSocketHeartbeat(ws);
|
|
@@ -1136,7 +1760,7 @@ export class RPCTransport {
|
|
|
1136
1760
|
if (method === 'challenge') {
|
|
1137
1761
|
this._challenge = message;
|
|
1138
1762
|
this._logger.debug('challenge received');
|
|
1139
|
-
this._dispatcher.
|
|
1763
|
+
this._dispatcher.enqueue('connection.challenge', message.params ?? {});
|
|
1140
1764
|
return;
|
|
1141
1765
|
}
|
|
1142
1766
|
// 事件消息(event/ 前缀)
|
|
@@ -1146,12 +1770,7 @@ export class RPCTransport {
|
|
|
1146
1770
|
this._logger.debug(`event recv: event=${sdkEvent} ${summarizeDict(message.params, DIAG_RESULT_FIELDS)}`);
|
|
1147
1771
|
const meta = message._meta;
|
|
1148
1772
|
if (this._metaObserver !== null && meta !== null && typeof meta === 'object' && !Array.isArray(meta)) {
|
|
1149
|
-
|
|
1150
|
-
this._metaObserver(meta);
|
|
1151
|
-
}
|
|
1152
|
-
catch (err) {
|
|
1153
|
-
this._logger.debug(`event meta_observer raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
1154
|
-
}
|
|
1773
|
+
this._queueObserver('event meta_observer', this._metaObserver, meta);
|
|
1155
1774
|
}
|
|
1156
1775
|
// 提取事件中的 _trace 并回调 observer,然后从 params 中剥离
|
|
1157
1776
|
const params = (message.params ?? {});
|
|
@@ -1160,10 +1779,9 @@ export class RPCTransport {
|
|
|
1160
1779
|
delete params._trace;
|
|
1161
1780
|
if (eventTrace && typeof eventTrace === 'object' && !Array.isArray(eventTrace)) {
|
|
1162
1781
|
if (this._traceObserver !== null) {
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
}
|
|
1166
|
-
catch { /* observer 抛错被吞 */ }
|
|
1782
|
+
this._queueObserver('trace_observer', this._traceObserver, {
|
|
1783
|
+
type: 'event', event: sdkEvent, trace: eventTrace,
|
|
1784
|
+
});
|
|
1167
1785
|
}
|
|
1168
1786
|
const traceObj = eventTrace;
|
|
1169
1787
|
this._logger.info(`[trace=${String(traceObj.trace_id ?? '')}] event_recv event=${sdkEvent}`);
|
|
@@ -1171,23 +1789,18 @@ export class RPCTransport {
|
|
|
1171
1789
|
}
|
|
1172
1790
|
// 发布为 _raw.{event},由 AUNClient 处理后再发布用户可见的事件
|
|
1173
1791
|
if (sdkEvent.startsWith('app.')) {
|
|
1174
|
-
this._dispatcher.
|
|
1792
|
+
this._dispatcher.enqueue(sdkEvent, params);
|
|
1175
1793
|
return;
|
|
1176
1794
|
}
|
|
1177
|
-
this._dispatcher.
|
|
1795
|
+
this._dispatcher.enqueue(`_raw.${sdkEvent}`, params);
|
|
1178
1796
|
return;
|
|
1179
1797
|
}
|
|
1180
1798
|
// 其他通知
|
|
1181
1799
|
const meta = message._meta;
|
|
1182
1800
|
if (this._metaObserver !== null && meta !== null && typeof meta === 'object' && !Array.isArray(meta)) {
|
|
1183
|
-
|
|
1184
|
-
this._metaObserver(meta);
|
|
1185
|
-
}
|
|
1186
|
-
catch (err) {
|
|
1187
|
-
this._logger.debug(`notification meta_observer raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
1188
|
-
}
|
|
1801
|
+
this._queueObserver('notification meta_observer', this._metaObserver, meta);
|
|
1189
1802
|
}
|
|
1190
|
-
this._dispatcher.
|
|
1803
|
+
this._dispatcher.enqueue('notification', message);
|
|
1191
1804
|
}
|
|
1192
1805
|
/** 解码 WebSocket 消息为 JSON 对象 */
|
|
1193
1806
|
_decodeMessage(raw) {
|
|
@@ -1222,4 +1835,116 @@ export class RPCTransport {
|
|
|
1222
1835
|
}
|
|
1223
1836
|
}
|
|
1224
1837
|
}
|
|
1838
|
+
if (!isMainThread && Boolean(workerData?.aunShortRpcWorker) && parentPort) {
|
|
1839
|
+
const command = workerData;
|
|
1840
|
+
const controller = new AbortController();
|
|
1841
|
+
void executeShortRpcEnvelope(command.url, command.method, command.params, controller.signal)
|
|
1842
|
+
.then((result) => parentPort.postMessage({
|
|
1843
|
+
type: 'reply', id: command.id, ok: true, result,
|
|
1844
|
+
}))
|
|
1845
|
+
.catch((error) => parentPort.postMessage({
|
|
1846
|
+
type: 'reply', id: command.id, ok: false, error: serializeWorkerError(error),
|
|
1847
|
+
}))
|
|
1848
|
+
.finally(() => parentPort.close());
|
|
1849
|
+
}
|
|
1850
|
+
if (!isMainThread && Boolean(workerData?.aunTransportWorker) && parentPort) {
|
|
1851
|
+
let workerActor = null;
|
|
1852
|
+
const bridgeGeneration = () => Number(workerActor?._connectionGeneration ?? 0);
|
|
1853
|
+
const workerDispatcher = {
|
|
1854
|
+
enqueue(event, payload) {
|
|
1855
|
+
if (event === '_transport.observer') {
|
|
1856
|
+
const item = payload;
|
|
1857
|
+
parentPort.postMessage({ type: 'event', generation: bridgeGeneration(), event, payload: {
|
|
1858
|
+
kind: item.kind ?? 'observer', payload: item.payload ?? {},
|
|
1859
|
+
} });
|
|
1860
|
+
return;
|
|
1861
|
+
}
|
|
1862
|
+
parentPort.postMessage({ type: 'event', generation: bridgeGeneration(), event, payload });
|
|
1863
|
+
},
|
|
1864
|
+
subscribe() { return { unsubscribe() { } }; },
|
|
1865
|
+
};
|
|
1866
|
+
const actor = new RPCTransport({
|
|
1867
|
+
eventDispatcher: workerDispatcher,
|
|
1868
|
+
timeout: Number(workerData.timeout ?? 10_000),
|
|
1869
|
+
verifySsl: Boolean(workerData.verifySsl ?? true),
|
|
1870
|
+
// Worker 内只让 close handler 经 workerDispatcher 转发一次,主线程负责执行真实回调。
|
|
1871
|
+
onDisconnect: () => { },
|
|
1872
|
+
});
|
|
1873
|
+
workerActor = actor;
|
|
1874
|
+
actor.setMetaObserver((meta) => workerDispatcher.enqueue('_transport.observer', {
|
|
1875
|
+
kind: 'meta_observer', payload: meta,
|
|
1876
|
+
}));
|
|
1877
|
+
actor.setTraceObserver((trace) => workerDispatcher.enqueue('_transport.observer', {
|
|
1878
|
+
kind: 'trace_observer', payload: trace,
|
|
1879
|
+
}));
|
|
1880
|
+
const active = new Map();
|
|
1881
|
+
let dnsFallback = null;
|
|
1882
|
+
actor._dnsNet = {
|
|
1883
|
+
loadDnsCache(hostname) {
|
|
1884
|
+
return dnsFallback?.hostname === hostname ? { ip: dnsFallback.ip, port: dnsFallback.port } : null;
|
|
1885
|
+
},
|
|
1886
|
+
};
|
|
1887
|
+
parentPort.on('message', async (command) => {
|
|
1888
|
+
const id = command.id;
|
|
1889
|
+
try {
|
|
1890
|
+
if (command.type === 'cancel') {
|
|
1891
|
+
const pending = active.get(String(command.targetId ?? ''));
|
|
1892
|
+
pending?.cancel?.();
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
let result;
|
|
1896
|
+
switch (command.type) {
|
|
1897
|
+
case 'connect': {
|
|
1898
|
+
dnsFallback = command.dnsFallback ?? null;
|
|
1899
|
+
const request = actor.connect(String(command.url ?? ''));
|
|
1900
|
+
if (id)
|
|
1901
|
+
active.set(id, { cancel: request.cancel ?? (() => { void actor.close().catch(() => { }); }) });
|
|
1902
|
+
result = await request;
|
|
1903
|
+
if (id)
|
|
1904
|
+
active.delete(id);
|
|
1905
|
+
break;
|
|
1906
|
+
}
|
|
1907
|
+
case 'call': {
|
|
1908
|
+
const request = actor.call(String(command.method ?? ''), command.params ?? undefined, command.timeout, command.trace, Boolean(command.background));
|
|
1909
|
+
if (id)
|
|
1910
|
+
active.set(id, request);
|
|
1911
|
+
result = await request;
|
|
1912
|
+
if (id)
|
|
1913
|
+
active.delete(id);
|
|
1914
|
+
break;
|
|
1915
|
+
}
|
|
1916
|
+
case 'notify':
|
|
1917
|
+
result = await actor.notify(String(command.method ?? ''), command.params);
|
|
1918
|
+
break;
|
|
1919
|
+
case 'close':
|
|
1920
|
+
result = await actor.close();
|
|
1921
|
+
break;
|
|
1922
|
+
case 'set_timeout':
|
|
1923
|
+
actor.setTimeout(Number(command.value));
|
|
1924
|
+
return;
|
|
1925
|
+
case 'set_connect_timeout':
|
|
1926
|
+
actor.setConnectTimeout(Number(command.value));
|
|
1927
|
+
return;
|
|
1928
|
+
case 'set_trace_mode':
|
|
1929
|
+
actor.setTraceMode(String(command.value ?? 'off'));
|
|
1930
|
+
return;
|
|
1931
|
+
default: return;
|
|
1932
|
+
}
|
|
1933
|
+
if (id)
|
|
1934
|
+
parentPort.postMessage({ type: 'reply', id, generation: bridgeGeneration(), ok: true, result });
|
|
1935
|
+
}
|
|
1936
|
+
catch (error) {
|
|
1937
|
+
if (id)
|
|
1938
|
+
active.delete(id);
|
|
1939
|
+
if (id)
|
|
1940
|
+
parentPort.postMessage({
|
|
1941
|
+
type: 'reply',
|
|
1942
|
+
id,
|
|
1943
|
+
generation: bridgeGeneration(),
|
|
1944
|
+
ok: false,
|
|
1945
|
+
error: serializeWorkerError(error),
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1225
1950
|
//# sourceMappingURL=transport.js.map
|