@ray-js/t-agent-plugin-aistream 0.2.0-beta-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/LICENSE.md +21 -0
- package/README-zh_CN.md +12 -0
- package/README.md +12 -0
- package/dist/AIStreamTypes.d.ts +1413 -0
- package/dist/AIStreamTypes.js +216 -0
- package/dist/ChatHistoryStore.d.ts +66 -0
- package/dist/ChatHistoryStore.js +160 -0
- package/dist/global.d.ts +4 -0
- package/dist/global.js +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/polyfill.d.ts +1 -0
- package/dist/polyfill.js +8 -0
- package/dist/utils/AIStream.d.ts +137 -0
- package/dist/utils/AIStream.js +486 -0
- package/dist/utils/abort.d.ts +38 -0
- package/dist/utils/abort.js +177 -0
- package/dist/utils/actions.d.ts +48 -0
- package/dist/utils/actions.js +76 -0
- package/dist/utils/apis.d.ts +15 -0
- package/dist/utils/apis.js +10 -0
- package/dist/utils/defaultMock.d.ts +1 -0
- package/dist/utils/defaultMock.js +429 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +3 -0
- package/dist/utils/mock.d.ts +48 -0
- package/dist/utils/mock.js +72 -0
- package/dist/utils/observer.d.ts +61 -0
- package/dist/utils/observer.js +152 -0
- package/dist/utils/parsers.d.ts +10 -0
- package/dist/utils/parsers.js +13 -0
- package/dist/utils/promisify.d.ts +18 -0
- package/dist/utils/promisify.js +82 -0
- package/dist/utils/sendMessage.d.ts +21 -0
- package/dist/utils/sendMessage.js +241 -0
- package/dist/utils/ttt.d.ts +99 -0
- package/dist/utils/ttt.js +97 -0
- package/dist/utils/url.d.ts +11 -0
- package/dist/utils/url.js +31 -0
- package/dist/utils/version.d.ts +1 -0
- package/dist/utils/version.js +63 -0
- package/dist/withAIStream.d.ts +64 -0
- package/dist/withAIStream.js +420 -0
- package/package.json +39 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
3
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
4
|
+
import "core-js/modules/esnext.iterator.find.js";
|
|
5
|
+
import "core-js/modules/esnext.iterator.for-each.js";
|
|
6
|
+
import "core-js/modules/esnext.iterator.map.js";
|
|
7
|
+
import "core-js/modules/web.dom-collections.iterator.js";
|
|
8
|
+
import { AIStreamErrorCode, BizTag, ConnectClientType, ConnectState, EventType, SessionState } from '../AIStreamTypes';
|
|
9
|
+
import { closeSession, connect, createSession, disconnect, getCurrentHomeInfo, queryAgentToken, registerRecordAmplitudes, sendEventChatBreak, sendEventEnd, sendEventPayloadEnd, sendEventStart, sendImageData, sendTextData, startRecordAndSendAudioData, stopRecordAndSendAudioData, unregisterVoiceAmplitudes } from './ttt';
|
|
10
|
+
import { AIStreamObserver, AIStreamObserverPool } from './observer';
|
|
11
|
+
import { isAbortError } from '@ray-js/t-agent';
|
|
12
|
+
export class AIStreamClient {
|
|
13
|
+
constructor() {
|
|
14
|
+
_defineProperty(this, "pool", new AIStreamObserverPool());
|
|
15
|
+
_defineProperty(this, "cached", new Map());
|
|
16
|
+
}
|
|
17
|
+
getConnection(options) {
|
|
18
|
+
const key = "".concat(options.clientType, "|").concat(options.deviceId || '');
|
|
19
|
+
let connection = this.cached.get(key);
|
|
20
|
+
if (!connection) {
|
|
21
|
+
connection = new AIStreamConnection(this.pool, options);
|
|
22
|
+
}
|
|
23
|
+
return connection;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export class AIStreamConnection {
|
|
27
|
+
constructor(pool, options) {
|
|
28
|
+
_defineProperty(this, "connectionId", null);
|
|
29
|
+
_defineProperty(this, "state", ConnectState.INIT);
|
|
30
|
+
_defineProperty(this, "activeSessions", new Set());
|
|
31
|
+
_defineProperty(this, "promise", null);
|
|
32
|
+
_defineProperty(this, "observer", null);
|
|
33
|
+
_defineProperty(this, "onStateChanged", entry => {
|
|
34
|
+
if (entry.type === 'connectionState' && entry.body.connectionId === this.connectionId) {
|
|
35
|
+
this.activeSessions.forEach(session => {
|
|
36
|
+
if (session.sessionId) {
|
|
37
|
+
session._onStateChanged(entry);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (entry.body.connectState === ConnectState.DISCONNECTED || entry.body.connectState === ConnectState.CLOSED) {
|
|
41
|
+
// 事件触发的时候,只做清理
|
|
42
|
+
this.state = entry.body.connectState;
|
|
43
|
+
this.cleanup();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (entry.type === 'sessionState') {
|
|
47
|
+
this.activeSessions.forEach(session => {
|
|
48
|
+
if (session.sessionId && session.sessionId === entry.body.sessionId) {
|
|
49
|
+
session._onStateChanged(entry);
|
|
50
|
+
if (entry.body.sessionState === SessionState.CLOSED || entry.body.sessionState === SessionState.CREATE_FAILED) {
|
|
51
|
+
this.activeSessions.delete(session);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
this.pool = pool;
|
|
58
|
+
this.options = options;
|
|
59
|
+
}
|
|
60
|
+
_ensureConnected() {
|
|
61
|
+
if (this.promise) {
|
|
62
|
+
return this.promise;
|
|
63
|
+
}
|
|
64
|
+
if (this.state === ConnectState.CONNECTED) {
|
|
65
|
+
return Promise.resolve();
|
|
66
|
+
}
|
|
67
|
+
this.promise = (async () => {
|
|
68
|
+
// 监听断开事件,重置 state & 清理
|
|
69
|
+
this.observer = new AIStreamObserver(this.onStateChanged, this.pool);
|
|
70
|
+
this.observer.observe({
|
|
71
|
+
connectionState: true,
|
|
72
|
+
sessionState: true
|
|
73
|
+
});
|
|
74
|
+
// 调用 SDK connect
|
|
75
|
+
const res = await connect(this.options);
|
|
76
|
+
this.connectionId = res.connectionId;
|
|
77
|
+
this.state = ConnectState.CONNECTED;
|
|
78
|
+
this.promise = null;
|
|
79
|
+
})();
|
|
80
|
+
return this.promise;
|
|
81
|
+
}
|
|
82
|
+
cleanup() {
|
|
83
|
+
var _this$observer;
|
|
84
|
+
(_this$observer = this.observer) === null || _this$observer === void 0 || _this$observer.disconnect();
|
|
85
|
+
this.observer = null;
|
|
86
|
+
this.connectionId = null;
|
|
87
|
+
this.promise = null;
|
|
88
|
+
this.activeSessions.forEach(s => s.cleanup());
|
|
89
|
+
this.activeSessions.clear();
|
|
90
|
+
}
|
|
91
|
+
createSession(options) {
|
|
92
|
+
const session = new AIStreamSession(this, this.pool, options);
|
|
93
|
+
this.activeSessions.add(session);
|
|
94
|
+
return session;
|
|
95
|
+
}
|
|
96
|
+
async closeSession(session) {
|
|
97
|
+
if (session.sessionId && this.connectionId) {
|
|
98
|
+
await closeSession({
|
|
99
|
+
sessionId: session.sessionId,
|
|
100
|
+
code: AIStreamErrorCode.OK
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
this.activeSessions.delete(session);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 断连
|
|
107
|
+
async close() {
|
|
108
|
+
if (this.connectionId) {
|
|
109
|
+
// 只有设备端才需要断开连接,App 端由 App 来维持
|
|
110
|
+
if (this.options.clientType === ConnectClientType.DEVICE) {
|
|
111
|
+
await disconnect({
|
|
112
|
+
connectionId: this.connectionId
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
this.state = ConnectState.CLOSED;
|
|
116
|
+
this.cleanup();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export class AIStreamSession {
|
|
121
|
+
constructor(connection, pool, options) {
|
|
122
|
+
_defineProperty(this, "sessionId", null);
|
|
123
|
+
_defineProperty(this, "sendDataChannels", []);
|
|
124
|
+
_defineProperty(this, "revDataChannels", []);
|
|
125
|
+
_defineProperty(this, "promise", null);
|
|
126
|
+
_defineProperty(this, "_onStateChanged", entry => {
|
|
127
|
+
var _this$activeEvent;
|
|
128
|
+
(_this$activeEvent = this.activeEvent) === null || _this$activeEvent === void 0 || _this$activeEvent.emit('data', entry);
|
|
129
|
+
if (entry.type === 'sessionState' && (entry.body.sessionState === SessionState.CLOSED || entry.body.sessionState === SessionState.CREATE_FAILED)) {
|
|
130
|
+
this.cleanup();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
_defineProperty(this, "onDataEntry", entry => {
|
|
134
|
+
var _this$activeEvent2;
|
|
135
|
+
if (!this.activeEvent) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
(_this$activeEvent2 = this.activeEvent) === null || _this$activeEvent2 === void 0 || _this$activeEvent2.emit('data', entry);
|
|
139
|
+
if (entry.type === 'event' && entry.body.eventId === this.activeEvent.eventId) {
|
|
140
|
+
const {
|
|
141
|
+
eventType
|
|
142
|
+
} = entry.body;
|
|
143
|
+
if (eventType === EventType.EVENT_END || eventType === EventType.CHAT_BREAK || eventType === EventType.ONE_SHOT) {
|
|
144
|
+
var _this$activeEvent3;
|
|
145
|
+
(_this$activeEvent3 = this.activeEvent) === null || _this$activeEvent3 === void 0 || _this$activeEvent3.emit('finish');
|
|
146
|
+
this.cleanupEvent();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
this.connection = connection;
|
|
151
|
+
this.pool = pool;
|
|
152
|
+
this.options = options;
|
|
153
|
+
}
|
|
154
|
+
ensureSession() {
|
|
155
|
+
if (this.promise) {
|
|
156
|
+
return this.promise;
|
|
157
|
+
}
|
|
158
|
+
if (this.sessionId) {
|
|
159
|
+
return Promise.resolve();
|
|
160
|
+
}
|
|
161
|
+
this.promise = (async () => {
|
|
162
|
+
await this.connection._ensureConnected();
|
|
163
|
+
let ownerId = this.options.ownerId;
|
|
164
|
+
if (!ownerId) {
|
|
165
|
+
if (this.connection.options.clientType === ConnectClientType.APP) {
|
|
166
|
+
const {
|
|
167
|
+
homeId
|
|
168
|
+
} = await getCurrentHomeInfo();
|
|
169
|
+
ownerId = homeId;
|
|
170
|
+
} else {
|
|
171
|
+
ownerId = this.connection.options.deviceId;
|
|
172
|
+
}
|
|
173
|
+
this.options.ownerId = ownerId;
|
|
174
|
+
}
|
|
175
|
+
const options = _objectSpread({
|
|
176
|
+
bizTag: BizTag.DEFAULT,
|
|
177
|
+
ownerId,
|
|
178
|
+
extParams: {}
|
|
179
|
+
}, this.options);
|
|
180
|
+
const {
|
|
181
|
+
agentToken,
|
|
182
|
+
bizConfig
|
|
183
|
+
} = await queryAgentToken(options);
|
|
184
|
+
const res = await createSession({
|
|
185
|
+
bizTag: options.bizTag,
|
|
186
|
+
agentToken,
|
|
187
|
+
bizConfig,
|
|
188
|
+
userData: options.userData
|
|
189
|
+
});
|
|
190
|
+
this.sessionId = res.sessionId;
|
|
191
|
+
this.sendDataChannels = res.sendDataChannels;
|
|
192
|
+
this.revDataChannels = res.revDataChannels;
|
|
193
|
+
})();
|
|
194
|
+
return this.promise;
|
|
195
|
+
}
|
|
196
|
+
async startEvent() {
|
|
197
|
+
let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
198
|
+
if (this.activeEvent) {
|
|
199
|
+
throw new Error('Cannot start a new event while another is active');
|
|
200
|
+
}
|
|
201
|
+
await this.ensureSession();
|
|
202
|
+
const {
|
|
203
|
+
eventId
|
|
204
|
+
} = await sendEventStart(_objectSpread({
|
|
205
|
+
sessionId: this.sessionId
|
|
206
|
+
}, options));
|
|
207
|
+
this.activeEvent = new AIStreamEvent({
|
|
208
|
+
eventId,
|
|
209
|
+
sessionId: this.sessionId,
|
|
210
|
+
sendDataChannels: this.sendDataChannels
|
|
211
|
+
});
|
|
212
|
+
const onError = error => {
|
|
213
|
+
if (isAbortError(error)) {
|
|
214
|
+
this.cleanupEvent();
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
this.activeEvent.on('error', onError);
|
|
218
|
+
const observerOptions = {
|
|
219
|
+
event: true,
|
|
220
|
+
dataChannels: this.revDataChannels,
|
|
221
|
+
sessionId: this.sessionId
|
|
222
|
+
};
|
|
223
|
+
this.activeObserver = new AIStreamObserver(this.onDataEntry, this.pool);
|
|
224
|
+
for (const revDataChannel of this.revDataChannels) {
|
|
225
|
+
if (revDataChannel.startsWith('text')) {
|
|
226
|
+
observerOptions.text = true;
|
|
227
|
+
}
|
|
228
|
+
if (revDataChannel.startsWith('audio')) {
|
|
229
|
+
observerOptions.audio = true;
|
|
230
|
+
}
|
|
231
|
+
if (revDataChannel.startsWith('video')) {
|
|
232
|
+
observerOptions.video = true;
|
|
233
|
+
}
|
|
234
|
+
if (revDataChannel.startsWith('file')) {
|
|
235
|
+
observerOptions.file = true;
|
|
236
|
+
}
|
|
237
|
+
if (revDataChannel.startsWith('image')) {
|
|
238
|
+
observerOptions.image = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.activeObserver.observe(observerOptions);
|
|
242
|
+
return this.activeEvent;
|
|
243
|
+
}
|
|
244
|
+
cleanupEvent() {
|
|
245
|
+
var _this$activeObserver, _this$activeEvent4;
|
|
246
|
+
(_this$activeObserver = this.activeObserver) === null || _this$activeObserver === void 0 || _this$activeObserver.disconnect();
|
|
247
|
+
this.activeObserver = null;
|
|
248
|
+
(_this$activeEvent4 = this.activeEvent) === null || _this$activeEvent4 === void 0 || _this$activeEvent4.emit('close');
|
|
249
|
+
this.activeEvent = null;
|
|
250
|
+
}
|
|
251
|
+
cleanup() {
|
|
252
|
+
this.cleanupEvent();
|
|
253
|
+
this.sessionId = null;
|
|
254
|
+
this.sendDataChannels = [];
|
|
255
|
+
this.revDataChannels = [];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// 会话关闭清理
|
|
259
|
+
async close() {
|
|
260
|
+
await this.connection.closeSession(this);
|
|
261
|
+
if (this.sessionId) {
|
|
262
|
+
this.cleanup();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export class AIStreamEvent {
|
|
267
|
+
constructor(init) {
|
|
268
|
+
_defineProperty(this, "closed", false);
|
|
269
|
+
_defineProperty(this, "chains", Object.create(null));
|
|
270
|
+
_defineProperty(this, "streams", Object.create(null));
|
|
271
|
+
_defineProperty(this, "listeners", {
|
|
272
|
+
finish: new Set(),
|
|
273
|
+
error: new Set(),
|
|
274
|
+
data: new Set(),
|
|
275
|
+
close: new Set()
|
|
276
|
+
});
|
|
277
|
+
this.eventId = init.eventId;
|
|
278
|
+
this.sessionId = init.sessionId;
|
|
279
|
+
this.sendDataChannels = init.sendDataChannels;
|
|
280
|
+
}
|
|
281
|
+
findFirstCode(type) {
|
|
282
|
+
const code = this.sendDataChannels.find(code => code.startsWith(type));
|
|
283
|
+
if (!code) {
|
|
284
|
+
throw new Error("No available data code for type: ".concat(type));
|
|
285
|
+
}
|
|
286
|
+
return code;
|
|
287
|
+
}
|
|
288
|
+
write(chunk) {
|
|
289
|
+
if (this.closed) {
|
|
290
|
+
throw new Error('Cannot write to a closed event');
|
|
291
|
+
}
|
|
292
|
+
const dataChannel = chunk.dataChannel || this.findFirstCode(chunk.type);
|
|
293
|
+
let promise = this.chains[dataChannel];
|
|
294
|
+
if (!promise) {
|
|
295
|
+
promise = Promise.resolve();
|
|
296
|
+
}
|
|
297
|
+
this.chains[dataChannel] = promise.then(async () => {
|
|
298
|
+
if (this.closed) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (chunk.type === 'text') {
|
|
302
|
+
await sendTextData({
|
|
303
|
+
sessionId: this.sessionId,
|
|
304
|
+
text: chunk.text,
|
|
305
|
+
dataChannel,
|
|
306
|
+
userData: chunk.userData
|
|
307
|
+
});
|
|
308
|
+
} else if (chunk.type === 'file') {
|
|
309
|
+
// await sendFileData({
|
|
310
|
+
// sessionId: this.sessionId,
|
|
311
|
+
// path: chunk.path,
|
|
312
|
+
// format: chunk.format,
|
|
313
|
+
// dataChannel,
|
|
314
|
+
// userData: chunk.userData,
|
|
315
|
+
// });
|
|
316
|
+
} else if (chunk.type === 'image') {
|
|
317
|
+
await sendImageData({
|
|
318
|
+
sessionId: this.sessionId,
|
|
319
|
+
path: chunk.path,
|
|
320
|
+
userData: chunk.userData
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
await sendEventPayloadEnd({
|
|
324
|
+
eventId: this.eventId,
|
|
325
|
+
sessionId: this.sessionId,
|
|
326
|
+
dataChannel
|
|
327
|
+
});
|
|
328
|
+
}).catch(error => {
|
|
329
|
+
this.emit('error', error);
|
|
330
|
+
throw error;
|
|
331
|
+
});
|
|
332
|
+
return this.chains[dataChannel];
|
|
333
|
+
}
|
|
334
|
+
stream(source) {
|
|
335
|
+
if (this.closed) {
|
|
336
|
+
throw new Error('Cannot stream to a closed event');
|
|
337
|
+
}
|
|
338
|
+
const dataChannel = source.dataChannel || this.findFirstCode(source.type);
|
|
339
|
+
if (this.streams[dataChannel]) {
|
|
340
|
+
throw new Error("".concat(dataChannel, " stream already exists"));
|
|
341
|
+
}
|
|
342
|
+
const stream = {
|
|
343
|
+
dataChannel,
|
|
344
|
+
type: source.type,
|
|
345
|
+
started: false,
|
|
346
|
+
start: async () => {
|
|
347
|
+
if (this.closed) {
|
|
348
|
+
throw new Error('Cannot stream to a closed event');
|
|
349
|
+
}
|
|
350
|
+
if (stream.started) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
stream.started = true;
|
|
354
|
+
if (source.type === 'audio') {
|
|
355
|
+
if (source.amplitudeCount) {
|
|
356
|
+
await registerRecordAmplitudes({
|
|
357
|
+
count: source.amplitudeCount
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
await startRecordAndSendAudioData({
|
|
361
|
+
sessionId: this.sessionId,
|
|
362
|
+
dataChannel,
|
|
363
|
+
userData: source.userData
|
|
364
|
+
});
|
|
365
|
+
} else if (source.type === 'video') {
|
|
366
|
+
// const cameraType = source.cameraType || VideoCameraType.BACK;
|
|
367
|
+
// await startRecordAndSendVideoData({
|
|
368
|
+
// dataChannel,
|
|
369
|
+
// cameraType,
|
|
370
|
+
// userData: source.userData,
|
|
371
|
+
// });
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
stop: async () => {
|
|
375
|
+
if (!stream.started || this.closed) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
if (source.type === 'audio') {
|
|
379
|
+
if (source.amplitudeCount) {
|
|
380
|
+
await unregisterVoiceAmplitudes({
|
|
381
|
+
count: source.amplitudeCount
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
await stopRecordAndSendAudioData({
|
|
385
|
+
sessionId: this.sessionId,
|
|
386
|
+
dataChannel,
|
|
387
|
+
userData: source.userData
|
|
388
|
+
});
|
|
389
|
+
} else if (source.type === 'video') {
|
|
390
|
+
// const cameraType = source.cameraType || VideoCameraType.BACK;
|
|
391
|
+
// await stopRecordAndSendVideoData({
|
|
392
|
+
// dataChannel,
|
|
393
|
+
// cameraType,
|
|
394
|
+
// userData: source.userData,
|
|
395
|
+
// });
|
|
396
|
+
}
|
|
397
|
+
await sendEventPayloadEnd({
|
|
398
|
+
eventId: this.eventId,
|
|
399
|
+
sessionId: this.sessionId,
|
|
400
|
+
dataChannel
|
|
401
|
+
});
|
|
402
|
+
delete this.streams[dataChannel];
|
|
403
|
+
stream.started = false;
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
this.streams[dataChannel] = stream;
|
|
407
|
+
return stream;
|
|
408
|
+
}
|
|
409
|
+
async end(options) {
|
|
410
|
+
if (this.closed) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
await Promise.all([...Object.values(this.chains), ...Object.values(this.streams).map(s => s.stop())]);
|
|
414
|
+
await sendEventEnd({
|
|
415
|
+
eventId: this.eventId,
|
|
416
|
+
sessionId: this.sessionId,
|
|
417
|
+
userData: options === null || options === void 0 ? void 0 : options.userData
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
async abort(options) {
|
|
421
|
+
if (this.sessionId) {
|
|
422
|
+
await sendEventChatBreak({
|
|
423
|
+
eventId: this.eventId,
|
|
424
|
+
sessionId: this.sessionId,
|
|
425
|
+
userData: options === null || options === void 0 ? void 0 : options.userData
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const error = new Error('This operation was aborted');
|
|
429
|
+
error.name = 'AbortError';
|
|
430
|
+
// 发送 break 后,stream 自动会关掉,在这里提前关闭
|
|
431
|
+
Object.values(this.streams).forEach(s => {
|
|
432
|
+
s.started = false;
|
|
433
|
+
});
|
|
434
|
+
this.emit('error', error);
|
|
435
|
+
this.emit('close');
|
|
436
|
+
}
|
|
437
|
+
on(name, callback) {
|
|
438
|
+
if (name === 'finish') {
|
|
439
|
+
this.listeners.finish.add(callback);
|
|
440
|
+
} else if (name === 'error') {
|
|
441
|
+
this.listeners.error.add(callback);
|
|
442
|
+
} else if (name === 'data') {
|
|
443
|
+
this.listeners.data.add(callback);
|
|
444
|
+
} else if (name === 'close') {
|
|
445
|
+
this.listeners.close.add(callback);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
off(name, callback) {
|
|
449
|
+
if (name === 'finish') {
|
|
450
|
+
this.listeners.finish.delete(callback);
|
|
451
|
+
} else if (name === 'error') {
|
|
452
|
+
this.listeners.error.delete(callback);
|
|
453
|
+
} else if (name === 'data') {
|
|
454
|
+
this.listeners.data.delete(callback);
|
|
455
|
+
} else if (name === 'close') {
|
|
456
|
+
this.listeners.close.delete(callback);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
emit(name, data) {
|
|
460
|
+
if (this.closed) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (name === 'finish') {
|
|
464
|
+
this.listeners.finish.forEach(cb => cb());
|
|
465
|
+
this.emit('close');
|
|
466
|
+
} else if (name === 'error') {
|
|
467
|
+
this.listeners.error.forEach(cb => cb(data));
|
|
468
|
+
} else if (name === 'data') {
|
|
469
|
+
this.listeners.data.forEach(cb => cb(data));
|
|
470
|
+
} else if (name === 'close') {
|
|
471
|
+
this.closed = true;
|
|
472
|
+
this.listeners.close.forEach(cb => cb());
|
|
473
|
+
this.dispose();
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
dispose() {
|
|
477
|
+
this.sessionId = null;
|
|
478
|
+
this.listeners.finish.clear();
|
|
479
|
+
this.listeners.error.clear();
|
|
480
|
+
this.listeners.data.clear();
|
|
481
|
+
this.listeners.close.clear();
|
|
482
|
+
Object.values(this.streams).forEach(s => s.stop());
|
|
483
|
+
this.streams = Object.create(null);
|
|
484
|
+
this.chains = Object.create(null);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Emitter, EmitterEvent, AbortSignalObject } from '@ray-js/t-agent';
|
|
2
|
+
type EventCallback = (event: EmitterEvent) => void;
|
|
3
|
+
export declare class AbortSignal extends Emitter implements AbortSignalObject {
|
|
4
|
+
aborted: boolean;
|
|
5
|
+
onabort: EventCallback;
|
|
6
|
+
reason: any;
|
|
7
|
+
constructor();
|
|
8
|
+
toString(): string;
|
|
9
|
+
dispatchEvent(event: EmitterEvent): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* @see {@link https://developer.mozilla.org/zh-CN/docs/Web/API/AbortSignal/throwIfAborted}
|
|
12
|
+
*/
|
|
13
|
+
throwIfAborted(): void;
|
|
14
|
+
/**
|
|
15
|
+
* @see {@link https://developer.mozilla.org/zh-CN/docs/Web/API/AbortSignal/timeout_static}
|
|
16
|
+
* @param {number} time The "active" time in milliseconds before the returned {@link AbortSignalObject} will abort.
|
|
17
|
+
* The value must be within range of 0 and {@link Number.MAX_SAFE_INTEGER}.
|
|
18
|
+
* @returns {AbortSignalObject} The signal will abort with its {@link AbortSignalObject.reason} property set to a `TimeoutError` {@link DOMException} on timeout,
|
|
19
|
+
* or an `AbortError` {@link DOMException} if the operation was user-triggered.
|
|
20
|
+
*/
|
|
21
|
+
static timeout(time: number): AbortSignal;
|
|
22
|
+
/**
|
|
23
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static}
|
|
24
|
+
* @param {Iterable<AbortSignalObject>} iterable An {@link Iterable} (such as an {@link Array}) of abort signals.
|
|
25
|
+
* @returns {AbortSignalObject} - **Already aborted**, if any of the abort signals given is already aborted.
|
|
26
|
+
* The returned {@link AbortSignalObject}'s reason will be already set to the `reason` of the first abort signal that was already aborted.
|
|
27
|
+
* - **Asynchronously aborted**, when any abort signal in `iterable` aborts.
|
|
28
|
+
* The `reason` will be set to the reason of the first abort signal that is aborted.
|
|
29
|
+
*/
|
|
30
|
+
static any(iterable: Iterable<AbortSignal>): AbortSignal;
|
|
31
|
+
}
|
|
32
|
+
export declare class AbortController {
|
|
33
|
+
signal: AbortSignal;
|
|
34
|
+
constructor();
|
|
35
|
+
abort(reason: any): void;
|
|
36
|
+
toString(): string;
|
|
37
|
+
}
|
|
38
|
+
export default AbortController;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import "core-js/modules/es.symbol.description.js";
|
|
2
|
+
import "core-js/modules/web.dom-collections.iterator.js";
|
|
3
|
+
// source: https://github.com/mo/abortcontroller-polyfill
|
|
4
|
+
import { Emitter } from '@ray-js/t-agent';
|
|
5
|
+
function createAbortEvent(reason) {
|
|
6
|
+
let event;
|
|
7
|
+
try {
|
|
8
|
+
event = new Event('abort');
|
|
9
|
+
} catch (e) {
|
|
10
|
+
if (typeof document !== 'undefined') {
|
|
11
|
+
if (!document.createEvent) {
|
|
12
|
+
// For Internet Explorer 8:
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
event = document.createEventObject();
|
|
15
|
+
event.type = 'abort';
|
|
16
|
+
} else {
|
|
17
|
+
// For Internet Explorer 11:
|
|
18
|
+
event = document.createEvent('Event');
|
|
19
|
+
event.initEvent('abort', false, false);
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
// Fallback where document isn't available:
|
|
23
|
+
event = {
|
|
24
|
+
type: 'abort',
|
|
25
|
+
bubbles: false,
|
|
26
|
+
cancelable: false
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
event.reason = reason;
|
|
31
|
+
return event;
|
|
32
|
+
}
|
|
33
|
+
function normalizeAbortReason(reason) {
|
|
34
|
+
if (reason === undefined) {
|
|
35
|
+
if (typeof document === 'undefined') {
|
|
36
|
+
reason = new Error('This operation was aborted');
|
|
37
|
+
reason.name = 'AbortError';
|
|
38
|
+
} else {
|
|
39
|
+
try {
|
|
40
|
+
reason = new DOMException('signal is aborted without reason');
|
|
41
|
+
// The DOMException does not support setting the name property directly.
|
|
42
|
+
Object.defineProperty(reason, 'name', {
|
|
43
|
+
value: 'AbortError'
|
|
44
|
+
});
|
|
45
|
+
} catch (err) {
|
|
46
|
+
// IE 11 does not support calling the DOMException constructor, use a
|
|
47
|
+
// regular error object on it instead.
|
|
48
|
+
reason = new Error('This operation was aborted');
|
|
49
|
+
reason.name = 'AbortError';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return reason;
|
|
54
|
+
}
|
|
55
|
+
export class AbortSignal extends Emitter {
|
|
56
|
+
constructor() {
|
|
57
|
+
super();
|
|
58
|
+
// Some versions of babel does not transpile super() correctly for IE <= 10, if the parent
|
|
59
|
+
// constructor has failed to run, then "this.listeners" will still be undefined and then we call
|
|
60
|
+
// the parent constructor directly instead as a workaround. For general details, see babel bug:
|
|
61
|
+
// https://github.com/babel/babel/issues/3041
|
|
62
|
+
// This hack was added as a fix for the issue described here:
|
|
63
|
+
// https://github.com/Financial-Times/polyfill-library/pull/59#issuecomment-477558042
|
|
64
|
+
if (!this.listeners) {
|
|
65
|
+
Emitter.call(this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
|
|
69
|
+
// we want Object.keys(new AbortController().signal) to be [] for compat with the native impl
|
|
70
|
+
Object.defineProperty(this, 'aborted', {
|
|
71
|
+
value: false,
|
|
72
|
+
writable: true,
|
|
73
|
+
configurable: true
|
|
74
|
+
});
|
|
75
|
+
Object.defineProperty(this, 'onabort', {
|
|
76
|
+
value: null,
|
|
77
|
+
writable: true,
|
|
78
|
+
configurable: true
|
|
79
|
+
});
|
|
80
|
+
Object.defineProperty(this, 'reason', {
|
|
81
|
+
value: undefined,
|
|
82
|
+
writable: true,
|
|
83
|
+
configurable: true
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
toString() {
|
|
87
|
+
return '[object AbortSignal]';
|
|
88
|
+
}
|
|
89
|
+
dispatchEvent(event) {
|
|
90
|
+
if (event.type === 'abort') {
|
|
91
|
+
this.aborted = true;
|
|
92
|
+
if (typeof this.onabort === 'function') {
|
|
93
|
+
this.onabort.call(this, event);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return super.dispatchEvent(event);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @see {@link https://developer.mozilla.org/zh-CN/docs/Web/API/AbortSignal/throwIfAborted}
|
|
101
|
+
*/
|
|
102
|
+
throwIfAborted() {
|
|
103
|
+
const {
|
|
104
|
+
aborted,
|
|
105
|
+
reason = 'Aborted'
|
|
106
|
+
} = this;
|
|
107
|
+
if (!aborted) return;
|
|
108
|
+
throw reason;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @see {@link https://developer.mozilla.org/zh-CN/docs/Web/API/AbortSignal/timeout_static}
|
|
113
|
+
* @param {number} time The "active" time in milliseconds before the returned {@link AbortSignalObject} will abort.
|
|
114
|
+
* The value must be within range of 0 and {@link Number.MAX_SAFE_INTEGER}.
|
|
115
|
+
* @returns {AbortSignalObject} The signal will abort with its {@link AbortSignalObject.reason} property set to a `TimeoutError` {@link DOMException} on timeout,
|
|
116
|
+
* or an `AbortError` {@link DOMException} if the operation was user-triggered.
|
|
117
|
+
*/
|
|
118
|
+
static timeout(time) {
|
|
119
|
+
const controller = new AbortController();
|
|
120
|
+
setTimeout(() => controller.abort(new DOMException("This signal is timeout in ".concat(time, "ms"), 'TimeoutError')), time);
|
|
121
|
+
return controller.signal;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/any_static}
|
|
126
|
+
* @param {Iterable<AbortSignalObject>} iterable An {@link Iterable} (such as an {@link Array}) of abort signals.
|
|
127
|
+
* @returns {AbortSignalObject} - **Already aborted**, if any of the abort signals given is already aborted.
|
|
128
|
+
* The returned {@link AbortSignalObject}'s reason will be already set to the `reason` of the first abort signal that was already aborted.
|
|
129
|
+
* - **Asynchronously aborted**, when any abort signal in `iterable` aborts.
|
|
130
|
+
* The `reason` will be set to the reason of the first abort signal that is aborted.
|
|
131
|
+
*/
|
|
132
|
+
static any(iterable) {
|
|
133
|
+
const controller = new AbortController();
|
|
134
|
+
function clean() {
|
|
135
|
+
for (const signal of iterable) signal.removeEventListener('abort', abort);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* @this AbortSignalObject
|
|
139
|
+
*/
|
|
140
|
+
function abort() {
|
|
141
|
+
controller.abort(this.reason);
|
|
142
|
+
clean();
|
|
143
|
+
}
|
|
144
|
+
for (const signal of iterable) if (signal.aborted) {
|
|
145
|
+
controller.abort(signal.reason);
|
|
146
|
+
break;
|
|
147
|
+
} else signal.addEventListener('abort', abort);
|
|
148
|
+
return controller.signal;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export class AbortController {
|
|
152
|
+
constructor() {
|
|
153
|
+
// Compared to assignment, Object.defineProperty makes properties non-enumerable by default and
|
|
154
|
+
// we want Object.keys(new AbortController()) to be [] for compat with the native impl
|
|
155
|
+
Object.defineProperty(this, 'signal', {
|
|
156
|
+
value: new AbortSignal(),
|
|
157
|
+
writable: true,
|
|
158
|
+
configurable: true
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
abort(reason) {
|
|
162
|
+
const signalReason = normalizeAbortReason(reason);
|
|
163
|
+
const event = createAbortEvent(signalReason);
|
|
164
|
+
this.signal.reason = signalReason;
|
|
165
|
+
this.signal.dispatchEvent(event);
|
|
166
|
+
}
|
|
167
|
+
toString() {
|
|
168
|
+
return '[object AbortController]';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
172
|
+
// These are necessary to make sure that we get correct output for:
|
|
173
|
+
// Object.prototype.toString.call(new AbortController())
|
|
174
|
+
AbortController.prototype[Symbol.toStringTag] = 'AbortController';
|
|
175
|
+
AbortSignal.prototype[Symbol.toStringTag] = 'AbortSignal';
|
|
176
|
+
}
|
|
177
|
+
export default AbortController;
|