@agentunion/fastaun-browser 0.5.9 → 0.5.11
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 +64 -0
- package/_packed_docs/CHANGELOG.md +64 -0
- package/_packed_docs/INDEX.md +3 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
- 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 +102 -8
- package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +3 -2
- package/_packed_docs/sdk/INDEX.md +2 -1
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +23 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +16 -2
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +1374 -267
- package/dist/client/delivery.d.ts +34 -11
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +390 -100
- 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.d.ts.map +1 -1
- package/dist/client/lifecycle.js +34 -38
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +70 -4
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +4 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +169 -38
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +83 -33
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +31 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +167 -7
- package/dist/events.js.map +1 -1
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +16 -2
- package/dist/register-flow.js.map +1 -1
- package/dist/transport.d.ts +10 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +427 -29
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +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
|
@@ -4,9 +4,311 @@ import { isJsonObject } from './types.js';
|
|
|
4
4
|
const MAX_WS_PAYLOAD_SIZE = 1_000_000;
|
|
5
5
|
const MAX_RPC_INFLIGHT = 16;
|
|
6
6
|
const MAX_BACKGROUND_RPC_INFLIGHT = 8;
|
|
7
|
+
const WORKER_START_TIMEOUT_MS = 2_000;
|
|
8
|
+
const WORKER_OPEN_TIMEOUT_MS = 2_000;
|
|
7
9
|
const _noopLog = { error: () => { }, warn: () => { }, info: () => { }, debug: () => { } };
|
|
8
10
|
/** RPC 请求递增计数器,避免 Date.now() 高并发碰撞 */
|
|
9
11
|
let _rpcIdCounter = 0;
|
|
12
|
+
const WORKER_WEBSOCKET_SOURCE = `
|
|
13
|
+
let socket = null;
|
|
14
|
+
self.postMessage({ type: 'ready' });
|
|
15
|
+
self.onmessage = (event) => {
|
|
16
|
+
const command = event.data || {};
|
|
17
|
+
if (command.type === 'connect') {
|
|
18
|
+
try {
|
|
19
|
+
socket = new WebSocket(command.url);
|
|
20
|
+
socket.onopen = () => self.postMessage({ type: 'open' });
|
|
21
|
+
socket.onmessage = (message) => self.postMessage({ type: 'message', data: message.data });
|
|
22
|
+
socket.onerror = () => self.postMessage({ type: 'error', message: 'websocket error' });
|
|
23
|
+
socket.onclose = (close) => self.postMessage({
|
|
24
|
+
type: 'close', code: close.code, reason: close.reason || '', wasClean: close.wasClean === true,
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
self.postMessage({ type: 'error', message: error instanceof Error ? error.message : String(error) });
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (command.type === 'send') {
|
|
32
|
+
try {
|
|
33
|
+
if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('websocket is not open');
|
|
34
|
+
socket.send(command.data);
|
|
35
|
+
self.postMessage({ type: 'send_result', id: command.id, ok: true });
|
|
36
|
+
} catch (error) {
|
|
37
|
+
self.postMessage({
|
|
38
|
+
type: 'send_result', id: command.id, ok: false,
|
|
39
|
+
error: error instanceof Error ? error.message : String(error),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (command.type === 'close' && socket) socket.close(command.code, command.reason);
|
|
45
|
+
};
|
|
46
|
+
`;
|
|
47
|
+
class WorkerWebSocketProxy {
|
|
48
|
+
static CONNECTING = 0;
|
|
49
|
+
static OPEN = 1;
|
|
50
|
+
static CLOSING = 2;
|
|
51
|
+
static CLOSED = 3;
|
|
52
|
+
readyState = WorkerWebSocketProxy.CONNECTING;
|
|
53
|
+
onopen = null;
|
|
54
|
+
onmessage = null;
|
|
55
|
+
onerror = null;
|
|
56
|
+
onclose = null;
|
|
57
|
+
_url;
|
|
58
|
+
_worker;
|
|
59
|
+
_workerUrl;
|
|
60
|
+
_nativeSocket = null;
|
|
61
|
+
_workerReady = false;
|
|
62
|
+
_opened = false;
|
|
63
|
+
_closedEventEmitted = false;
|
|
64
|
+
_workerStartTimer = null;
|
|
65
|
+
_workerOpenTimer = null;
|
|
66
|
+
_listeners = new Map();
|
|
67
|
+
_pendingSends = new Map();
|
|
68
|
+
_sendSeq = 0;
|
|
69
|
+
constructor(url) {
|
|
70
|
+
this._url = url;
|
|
71
|
+
this._workerUrl = URL.createObjectURL(new Blob([WORKER_WEBSOCKET_SOURCE], { type: 'text/javascript' }));
|
|
72
|
+
try {
|
|
73
|
+
this._worker = new Worker(this._workerUrl);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
URL.revokeObjectURL(this._workerUrl);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
const worker = this._worker;
|
|
80
|
+
worker.onmessage = (event) => this._handleWorkerMessage(event.data);
|
|
81
|
+
worker.onerror = (event) => {
|
|
82
|
+
event.preventDefault?.();
|
|
83
|
+
if (!this._opened) {
|
|
84
|
+
this._fallbackToNative();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this._fallbackToNative(true);
|
|
88
|
+
};
|
|
89
|
+
this._workerStartTimer = globalThis.setTimeout(() => this._fallbackToNative(), WORKER_START_TIMEOUT_MS);
|
|
90
|
+
}
|
|
91
|
+
send(data) {
|
|
92
|
+
if (this.readyState !== WorkerWebSocketProxy.OPEN)
|
|
93
|
+
return Promise.reject(new Error('websocket is not open'));
|
|
94
|
+
if (this._nativeSocket) {
|
|
95
|
+
try {
|
|
96
|
+
this._nativeSocket.send(data);
|
|
97
|
+
return Promise.resolve();
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const worker = this._worker;
|
|
104
|
+
if (!worker)
|
|
105
|
+
return Promise.reject(new Error('websocket worker is not available'));
|
|
106
|
+
const id = ++this._sendSeq;
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
this._pendingSends.set(id, { resolve, reject });
|
|
109
|
+
try {
|
|
110
|
+
worker.postMessage({ type: 'send', id, data });
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
this._pendingSends.delete(id);
|
|
114
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
close(code, reason) {
|
|
119
|
+
if (this.readyState === WorkerWebSocketProxy.CLOSED)
|
|
120
|
+
return;
|
|
121
|
+
this.readyState = WorkerWebSocketProxy.CLOSING;
|
|
122
|
+
if (this._nativeSocket) {
|
|
123
|
+
try {
|
|
124
|
+
this._nativeSocket.close(code, reason);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
this._emitClose(new CloseEvent('close', { code: code ?? 1000, reason: reason ?? '', wasClean: true }));
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!this._workerReady || !this._worker) {
|
|
132
|
+
this._disposeWorker();
|
|
133
|
+
this._emitClose(new CloseEvent('close', { code: code ?? 1000, reason: reason ?? '', wasClean: true }));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
this._worker.postMessage({ type: 'close', code, reason });
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
this._fallbackToNative();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
addEventListener(type, listener) {
|
|
144
|
+
const callback = typeof listener === 'function' ? listener : (event) => listener.handleEvent(event);
|
|
145
|
+
const listeners = this._listeners.get(type) ?? new Set();
|
|
146
|
+
listeners.add(callback);
|
|
147
|
+
this._listeners.set(type, listeners);
|
|
148
|
+
}
|
|
149
|
+
removeEventListener(type, listener) {
|
|
150
|
+
if (typeof listener === 'function')
|
|
151
|
+
this._listeners.get(type)?.delete(listener);
|
|
152
|
+
}
|
|
153
|
+
_handleWorkerMessage(reply) {
|
|
154
|
+
if (reply.type === 'ready') {
|
|
155
|
+
if (this._workerReady || !this._worker)
|
|
156
|
+
return;
|
|
157
|
+
this._workerReady = true;
|
|
158
|
+
this._clearWorkerStartTimer();
|
|
159
|
+
try {
|
|
160
|
+
this._worker.postMessage({ type: 'connect', url: this._url });
|
|
161
|
+
this._workerOpenTimer = globalThis.setTimeout(() => this._fallbackToNative(), WORKER_OPEN_TIMEOUT_MS);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
this._fallbackToNative();
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (reply.type === 'send_result' && reply.id !== undefined) {
|
|
169
|
+
const pending = this._pendingSends.get(reply.id);
|
|
170
|
+
if (!pending)
|
|
171
|
+
return;
|
|
172
|
+
this._pendingSends.delete(reply.id);
|
|
173
|
+
if (reply.ok)
|
|
174
|
+
pending.resolve();
|
|
175
|
+
else
|
|
176
|
+
pending.reject(new Error(reply.error || 'websocket send failed'));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (reply.type === 'open') {
|
|
180
|
+
this._clearWorkerOpenTimer();
|
|
181
|
+
this._opened = true;
|
|
182
|
+
this.readyState = WorkerWebSocketProxy.OPEN;
|
|
183
|
+
const event = new Event('open');
|
|
184
|
+
this.onopen?.(event);
|
|
185
|
+
this._emit('open', event);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (reply.type === 'message') {
|
|
189
|
+
const event = new MessageEvent('message', { data: reply.data });
|
|
190
|
+
this.onmessage?.(event);
|
|
191
|
+
this._emit('message', event);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (reply.type === 'error') {
|
|
195
|
+
if (!this._opened && this.readyState === WorkerWebSocketProxy.CONNECTING) {
|
|
196
|
+
this._fallbackToNative();
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
this._emitError(reply.message || 'websocket worker error');
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (reply.type === 'close') {
|
|
203
|
+
if (!this._opened && this.readyState === WorkerWebSocketProxy.CONNECTING) {
|
|
204
|
+
this._fallbackToNative();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this._emitClose(new CloseEvent('close', {
|
|
208
|
+
code: reply.code ?? 1006, reason: reply.reason ?? '', wasClean: reply.wasClean === true,
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
_fallbackToNative(force = false) {
|
|
213
|
+
if (this._nativeSocket || this.readyState === WorkerWebSocketProxy.CLOSED)
|
|
214
|
+
return;
|
|
215
|
+
if (!force && (this.readyState !== WorkerWebSocketProxy.CONNECTING || this._opened))
|
|
216
|
+
return;
|
|
217
|
+
const wasOpened = this._opened;
|
|
218
|
+
this._opened = false;
|
|
219
|
+
this.readyState = WorkerWebSocketProxy.CONNECTING;
|
|
220
|
+
this._disposeWorker();
|
|
221
|
+
try {
|
|
222
|
+
const socket = new WebSocket(this._url);
|
|
223
|
+
this._nativeSocket = socket;
|
|
224
|
+
socket.onopen = (event) => {
|
|
225
|
+
this._opened = true;
|
|
226
|
+
this.readyState = WorkerWebSocketProxy.OPEN;
|
|
227
|
+
if (!wasOpened) {
|
|
228
|
+
this.onopen?.(event);
|
|
229
|
+
this._emit('open', event);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
socket.onmessage = (event) => {
|
|
233
|
+
this.onmessage?.(event);
|
|
234
|
+
this._emit('message', event);
|
|
235
|
+
};
|
|
236
|
+
socket.onerror = (event) => {
|
|
237
|
+
this.onerror?.(event);
|
|
238
|
+
this._emit('error', event);
|
|
239
|
+
};
|
|
240
|
+
socket.onclose = (event) => this._emitClose(event);
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
this.readyState = WorkerWebSocketProxy.CLOSED;
|
|
244
|
+
this._emitError(error instanceof Error ? error.message : String(error));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
_emitClose(event) {
|
|
248
|
+
if (this._closedEventEmitted)
|
|
249
|
+
return;
|
|
250
|
+
this._closedEventEmitted = true;
|
|
251
|
+
this.readyState = WorkerWebSocketProxy.CLOSED;
|
|
252
|
+
for (const pending of this._pendingSends.values())
|
|
253
|
+
pending.reject(new Error('websocket closed'));
|
|
254
|
+
this._pendingSends.clear();
|
|
255
|
+
this.onclose?.(event);
|
|
256
|
+
this._emit('close', event);
|
|
257
|
+
this._disposeWorker();
|
|
258
|
+
this._nativeSocket = null;
|
|
259
|
+
}
|
|
260
|
+
_clearWorkerStartTimer() {
|
|
261
|
+
if (this._workerStartTimer === null)
|
|
262
|
+
return;
|
|
263
|
+
globalThis.clearTimeout(this._workerStartTimer);
|
|
264
|
+
this._workerStartTimer = null;
|
|
265
|
+
}
|
|
266
|
+
_clearWorkerOpenTimer() {
|
|
267
|
+
if (this._workerOpenTimer === null)
|
|
268
|
+
return;
|
|
269
|
+
globalThis.clearTimeout(this._workerOpenTimer);
|
|
270
|
+
this._workerOpenTimer = null;
|
|
271
|
+
}
|
|
272
|
+
_disposeWorker() {
|
|
273
|
+
this._clearWorkerStartTimer();
|
|
274
|
+
this._clearWorkerOpenTimer();
|
|
275
|
+
for (const pending of this._pendingSends.values())
|
|
276
|
+
pending.reject(new Error('websocket worker unavailable'));
|
|
277
|
+
this._pendingSends.clear();
|
|
278
|
+
const worker = this._worker;
|
|
279
|
+
this._worker = null;
|
|
280
|
+
if (worker) {
|
|
281
|
+
worker.onmessage = null;
|
|
282
|
+
worker.onerror = null;
|
|
283
|
+
worker.terminate();
|
|
284
|
+
}
|
|
285
|
+
const workerUrl = this._workerUrl;
|
|
286
|
+
this._workerUrl = null;
|
|
287
|
+
if (workerUrl && typeof URL.revokeObjectURL === 'function')
|
|
288
|
+
URL.revokeObjectURL(workerUrl);
|
|
289
|
+
}
|
|
290
|
+
_emitError(message) {
|
|
291
|
+
const event = new ErrorEvent('error', { message });
|
|
292
|
+
this.onerror?.(event);
|
|
293
|
+
this._emit('error', event);
|
|
294
|
+
}
|
|
295
|
+
_emit(type, event) {
|
|
296
|
+
for (const listener of [...(this._listeners.get(type) ?? [])])
|
|
297
|
+
listener(event);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
export function createTransportWebSocket(url) {
|
|
301
|
+
if (typeof Worker === 'function' && typeof Blob === 'function' && typeof URL !== 'undefined'
|
|
302
|
+
&& typeof URL.createObjectURL === 'function' && typeof URL.revokeObjectURL === 'function') {
|
|
303
|
+
try {
|
|
304
|
+
return new WorkerWebSocketProxy(url);
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// Worker/CSP 不可用时保持原生 WebSocket 路径。
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return new WebSocket(url);
|
|
311
|
+
}
|
|
10
312
|
// ── Trace 树状展示 ─────────────────────────────────────────────
|
|
11
313
|
const TRACE_SPAN_DETAIL_FIELDS = [
|
|
12
314
|
'method', 'route', 'namespace', 'instance_id', 'aid', 'caller_aid',
|
|
@@ -260,6 +562,10 @@ export class RPCTransport {
|
|
|
260
562
|
_traceMode = 'off';
|
|
261
563
|
// Trace observer:observer(traceInfo) 在每次 RPC/事件携带 _trace 时调用
|
|
262
564
|
_traceObserver = null;
|
|
565
|
+
// 每个 transport 实例一个轻量级网络 actor。命令只在此处启动网络操作,
|
|
566
|
+
// 不等待 RPC 响应,避免应用回调中的 send 反向占住收包路径。
|
|
567
|
+
_actorTail = Promise.resolve();
|
|
568
|
+
_actorBusy = false;
|
|
263
569
|
constructor(opts) {
|
|
264
570
|
this._dispatcher = opts.eventDispatcher;
|
|
265
571
|
this._timeout = opts.timeout ?? 10;
|
|
@@ -287,18 +593,21 @@ export class RPCTransport {
|
|
|
287
593
|
setMetaObserver(observer) {
|
|
288
594
|
this._metaObserver = observer;
|
|
289
595
|
}
|
|
290
|
-
|
|
291
|
-
|
|
596
|
+
_notifyMetaObserver(message) {
|
|
597
|
+
const observer = this._metaObserver;
|
|
598
|
+
if (observer === null)
|
|
292
599
|
return;
|
|
293
600
|
const meta = message._meta;
|
|
294
601
|
if (!isJsonObject(meta))
|
|
295
602
|
return;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
603
|
+
this._dispatcher.enqueueTask(async () => {
|
|
604
|
+
try {
|
|
605
|
+
await observer(meta);
|
|
606
|
+
}
|
|
607
|
+
catch (exc) {
|
|
608
|
+
this._log.debug(`meta_observer raised: ${String(exc)}`);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
302
611
|
}
|
|
303
612
|
/** 设置 trace 模式:off / log / diag */
|
|
304
613
|
setTraceMode(mode) {
|
|
@@ -335,7 +644,10 @@ export class RPCTransport {
|
|
|
335
644
|
* 连接到 WebSocket URL。
|
|
336
645
|
* 等待首条消息,若为 challenge 则返回,否则进入消息路由。
|
|
337
646
|
*/
|
|
338
|
-
|
|
647
|
+
connect(url) {
|
|
648
|
+
return this._enqueueActorStart(() => this._connectImpl(url));
|
|
649
|
+
}
|
|
650
|
+
async _connectImpl(url) {
|
|
339
651
|
const tStart = Date.now();
|
|
340
652
|
this._log.debug(`connect enter: url=${url}`);
|
|
341
653
|
const setup = await this._withConnectionSetup(async () => {
|
|
@@ -345,7 +657,7 @@ export class RPCTransport {
|
|
|
345
657
|
this._lastCloseCode = null;
|
|
346
658
|
this._lastCloseReason = '';
|
|
347
659
|
const handshake = new Promise((resolve, reject) => {
|
|
348
|
-
const ws =
|
|
660
|
+
const ws = createTransportWebSocket(url);
|
|
349
661
|
this._ws = ws;
|
|
350
662
|
this._closed = false;
|
|
351
663
|
let initialResolved = false;
|
|
@@ -489,7 +801,10 @@ export class RPCTransport {
|
|
|
489
801
|
return setup.handshake;
|
|
490
802
|
}
|
|
491
803
|
/** 关闭连接 */
|
|
492
|
-
|
|
804
|
+
close() {
|
|
805
|
+
return this._enqueueActor(() => this._closeImpl());
|
|
806
|
+
}
|
|
807
|
+
async _closeImpl() {
|
|
493
808
|
await this._withConnectionSetup(() => this._closeUnlocked());
|
|
494
809
|
}
|
|
495
810
|
/** 已持有连接建立串行权时关闭当前 WebSocket。 */
|
|
@@ -566,7 +881,10 @@ export class RPCTransport {
|
|
|
566
881
|
* 发起 JSON-RPC 2.0 调用。
|
|
567
882
|
* 返回 result 字段的值;若有 error 字段则抛出映射后的错误。
|
|
568
883
|
*/
|
|
569
|
-
|
|
884
|
+
call(method, params, timeout, trace, background = false) {
|
|
885
|
+
return this._enqueueActorStart(() => this._callImpl(method, params, timeout, trace, background), () => new TimeoutError(`rpc cancelled: ${method}`, { retryable: true }));
|
|
886
|
+
}
|
|
887
|
+
_callImpl(method, params, timeout, trace, background = false) {
|
|
570
888
|
if (this._closed || !this._ws) {
|
|
571
889
|
throw this._notConnectedError();
|
|
572
890
|
}
|
|
@@ -612,7 +930,7 @@ export class RPCTransport {
|
|
|
612
930
|
this._drainRpcQueue();
|
|
613
931
|
}, effectiveTimeout);
|
|
614
932
|
const pending = {
|
|
615
|
-
resolve:
|
|
933
|
+
resolve: (response) => {
|
|
616
934
|
clearTimeout(timer);
|
|
617
935
|
const elapsed = Date.now() - tStart;
|
|
618
936
|
if (response.error !== undefined) {
|
|
@@ -631,7 +949,7 @@ export class RPCTransport {
|
|
|
631
949
|
if (traceId) {
|
|
632
950
|
this._log.info(`[trace=${traceId}] rpc_recv method=${method} rpc_id=${rpcId} duration_ms=${elapsed} status=ok`);
|
|
633
951
|
}
|
|
634
|
-
|
|
952
|
+
this._notifyMetaObserver(response);
|
|
635
953
|
// 处理 success 路径的 _trace
|
|
636
954
|
const respTrace = response._trace;
|
|
637
955
|
if (respTrace && typeof respTrace === 'object' && !Array.isArray(respTrace)) {
|
|
@@ -671,7 +989,10 @@ export class RPCTransport {
|
|
|
671
989
|
return Object.assign(promise, { cancel: () => cancelRpc?.() });
|
|
672
990
|
}
|
|
673
991
|
/** 发送 JSON-RPC 2.0 Notification,不分配 id,也不等待响应。 */
|
|
674
|
-
|
|
992
|
+
notify(method, params) {
|
|
993
|
+
return this._enqueueActorStart(() => this._notifyImpl(method, params));
|
|
994
|
+
}
|
|
995
|
+
async _notifyImpl(method, params) {
|
|
675
996
|
if (this._closed || !this._ws) {
|
|
676
997
|
throw this._notConnectedError();
|
|
677
998
|
}
|
|
@@ -699,6 +1020,63 @@ export class RPCTransport {
|
|
|
699
1020
|
this._sendChain = run.catch(() => { });
|
|
700
1021
|
return run;
|
|
701
1022
|
}
|
|
1023
|
+
_enqueueActor(operation) {
|
|
1024
|
+
this._actorBusy = true;
|
|
1025
|
+
const run = this._actorTail.then(async () => {
|
|
1026
|
+
return await operation();
|
|
1027
|
+
}, async () => {
|
|
1028
|
+
return await operation();
|
|
1029
|
+
});
|
|
1030
|
+
const tail = run.then(() => undefined, () => undefined);
|
|
1031
|
+
this._actorTail = tail;
|
|
1032
|
+
void tail.then(() => {
|
|
1033
|
+
if (this._actorTail === tail)
|
|
1034
|
+
this._actorBusy = false;
|
|
1035
|
+
});
|
|
1036
|
+
return run;
|
|
1037
|
+
}
|
|
1038
|
+
_enqueueActorStart(operation, cancellationError) {
|
|
1039
|
+
let resolveResult;
|
|
1040
|
+
let rejectResult;
|
|
1041
|
+
const result = new Promise((resolve, reject) => { resolveResult = resolve; rejectResult = reject; });
|
|
1042
|
+
let cancel;
|
|
1043
|
+
let cancelRequested = false;
|
|
1044
|
+
const launch = () => {
|
|
1045
|
+
if (cancelRequested)
|
|
1046
|
+
return;
|
|
1047
|
+
try {
|
|
1048
|
+
const inner = operation();
|
|
1049
|
+
cancel = inner.cancel;
|
|
1050
|
+
inner.then(resolveResult, rejectResult);
|
|
1051
|
+
}
|
|
1052
|
+
catch (err) {
|
|
1053
|
+
rejectResult(err);
|
|
1054
|
+
}
|
|
1055
|
+
};
|
|
1056
|
+
if (this._actorBusy) {
|
|
1057
|
+
const gate = this._actorTail.then(launch, launch);
|
|
1058
|
+
const tail = gate.then(() => undefined, () => undefined);
|
|
1059
|
+
this._actorTail = tail;
|
|
1060
|
+
void tail.then(() => {
|
|
1061
|
+
if (this._actorTail === tail)
|
|
1062
|
+
this._actorBusy = false;
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
launch();
|
|
1067
|
+
}
|
|
1068
|
+
return Object.assign(result, {
|
|
1069
|
+
cancel: () => {
|
|
1070
|
+
if (cancelRequested)
|
|
1071
|
+
return;
|
|
1072
|
+
cancelRequested = true;
|
|
1073
|
+
if (cancel)
|
|
1074
|
+
cancel();
|
|
1075
|
+
else
|
|
1076
|
+
rejectResult(cancellationError?.() ?? new TimeoutError('rpc cancelled', { retryable: true }));
|
|
1077
|
+
},
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
702
1080
|
_sendText(payload, context, beforeSend) {
|
|
703
1081
|
return this._enqueueSend(async () => {
|
|
704
1082
|
if (beforeSend && !beforeSend()) {
|
|
@@ -709,7 +1087,7 @@ export class RPCTransport {
|
|
|
709
1087
|
}
|
|
710
1088
|
const ws = this._ws;
|
|
711
1089
|
try {
|
|
712
|
-
ws.send(payload);
|
|
1090
|
+
await Promise.resolve(ws.send(payload));
|
|
713
1091
|
}
|
|
714
1092
|
catch (err) {
|
|
715
1093
|
throw await this._sendFailureError(context, err, ws);
|
|
@@ -841,7 +1219,15 @@ export class RPCTransport {
|
|
|
841
1219
|
const enriched = { ...respTrace, spans };
|
|
842
1220
|
this._log.info(traceDisplay(method, status, elapsedMs, respTrace, spans));
|
|
843
1221
|
if (this._traceObserver !== null) {
|
|
844
|
-
this._traceObserver
|
|
1222
|
+
const observer = this._traceObserver;
|
|
1223
|
+
this._dispatcher.enqueueTask(async () => {
|
|
1224
|
+
try {
|
|
1225
|
+
await observer({ type: 'rpc', method, trace: enriched, status, duration_ms: elapsedMs });
|
|
1226
|
+
}
|
|
1227
|
+
catch (err) {
|
|
1228
|
+
this._log.debug(`trace observer raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
845
1231
|
}
|
|
846
1232
|
}
|
|
847
1233
|
catch (err) {
|
|
@@ -887,10 +1273,18 @@ export class RPCTransport {
|
|
|
887
1273
|
// 非主动关闭时触发断线回调
|
|
888
1274
|
if (!wasClosed) {
|
|
889
1275
|
const error = new ConnectionError(`websocket closed: code=${event.code} reason=${event.reason}`);
|
|
890
|
-
this._dispatcher.publish('connection.error', { error });
|
|
891
1276
|
if (this._onDisconnect) {
|
|
892
|
-
|
|
1277
|
+
const onDisconnect = this._onDisconnect;
|
|
1278
|
+
this._dispatcher.enqueueTask(async () => {
|
|
1279
|
+
try {
|
|
1280
|
+
await onDisconnect(error, event.code);
|
|
1281
|
+
}
|
|
1282
|
+
catch (exc) {
|
|
1283
|
+
this._log.warn('[aun_core.transport] disconnect callback exception:', exc);
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
893
1286
|
}
|
|
1287
|
+
this._dispatcher.enqueue('connection.error', { error });
|
|
894
1288
|
}
|
|
895
1289
|
}
|
|
896
1290
|
_notConnectedError() {
|
|
@@ -922,7 +1316,7 @@ export class RPCTransport {
|
|
|
922
1316
|
if (method === 'challenge') {
|
|
923
1317
|
this._challenge = message;
|
|
924
1318
|
this._log.debug('challenge received');
|
|
925
|
-
this._dispatcher.
|
|
1319
|
+
this._dispatcher.enqueue('connection.challenge', message.params ?? {});
|
|
926
1320
|
return;
|
|
927
1321
|
}
|
|
928
1322
|
// 事件推送
|
|
@@ -930,7 +1324,7 @@ export class RPCTransport {
|
|
|
930
1324
|
const protocolEvent = method.slice(6);
|
|
931
1325
|
const sdkEvent = EVENT_NAME_MAP[protocolEvent] ?? protocolEvent;
|
|
932
1326
|
this._log.debug(`event recv: event=${sdkEvent} ${summarizeDict(message.params, DIAG_RESULT_FIELDS)}`);
|
|
933
|
-
|
|
1327
|
+
this._notifyMetaObserver(message);
|
|
934
1328
|
// 提取事件中的 _trace 并回调 observer,然后从 params 中剥离
|
|
935
1329
|
const params = (message.params ?? {});
|
|
936
1330
|
if ('_trace' in params) {
|
|
@@ -938,10 +1332,14 @@ export class RPCTransport {
|
|
|
938
1332
|
delete params._trace;
|
|
939
1333
|
if (eventTrace && typeof eventTrace === 'object' && !Array.isArray(eventTrace)) {
|
|
940
1334
|
if (this._traceObserver !== null) {
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1335
|
+
const observer = this._traceObserver;
|
|
1336
|
+
const tracePayload = eventTrace;
|
|
1337
|
+
this._dispatcher.enqueueTask(async () => {
|
|
1338
|
+
try {
|
|
1339
|
+
await observer({ type: 'event', event: sdkEvent, trace: tracePayload });
|
|
1340
|
+
}
|
|
1341
|
+
catch { /* observer 抛错被吞 */ }
|
|
1342
|
+
});
|
|
945
1343
|
}
|
|
946
1344
|
const traceObj = eventTrace;
|
|
947
1345
|
this._log.info(`[trace=${String(traceObj.trace_id ?? '')}] event_recv event=${sdkEvent}`);
|
|
@@ -949,16 +1347,16 @@ export class RPCTransport {
|
|
|
949
1347
|
}
|
|
950
1348
|
// 发布为 _raw.{event},由 AUNClient 处理后再发布用户可见的事件
|
|
951
1349
|
if (sdkEvent.startsWith('app.')) {
|
|
952
|
-
this._dispatcher.
|
|
1350
|
+
this._dispatcher.enqueue(sdkEvent, params);
|
|
953
1351
|
return;
|
|
954
1352
|
}
|
|
955
|
-
this._dispatcher.
|
|
1353
|
+
this._dispatcher.enqueue(`_raw.${sdkEvent}`, params);
|
|
956
1354
|
return;
|
|
957
1355
|
}
|
|
958
1356
|
// 其他通知
|
|
959
|
-
|
|
1357
|
+
this._notifyMetaObserver(message);
|
|
960
1358
|
this._log.debug(`notification recv: method=${method || '<no-method>'}`);
|
|
961
|
-
this._dispatcher.
|
|
1359
|
+
this._dispatcher.enqueue('notification', message);
|
|
962
1360
|
}
|
|
963
1361
|
_decodeMessage(raw) {
|
|
964
1362
|
if (isJsonObject(raw)) {
|