@xmov/avatar 2.4.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/avatar.cjs +1 -1
- package/dist/agent/avatar.modern.js +1 -1
- package/dist/agent/avatar.module.js +1 -1
- package/dist/agent/avatar.umd.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.modern.js +1 -1
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/modules/ResourceManager.d.ts +3 -1
- package/dist/utils/request.d.ts +2 -0
- package/package.json +1 -1
- package/src/index.ts +26 -15
- package/src/modules/ResourceManager.ts +19 -2
- package/src/utils/request.ts +5 -1
|
@@ -237,7 +237,9 @@ export default class ResourceManager {
|
|
|
237
237
|
load(onDownloadProgress?: TDownloadProgress): Promise<ISessionResponse | null>;
|
|
238
238
|
startSession(): Promise<any>;
|
|
239
239
|
_startSession(): Promise<any> | null;
|
|
240
|
-
stopSession(stop_reason: string
|
|
240
|
+
stopSession(stop_reason: string, options?: {
|
|
241
|
+
keepalive?: boolean;
|
|
242
|
+
}): Promise<any>;
|
|
241
243
|
/**
|
|
242
244
|
* 加载表情数据
|
|
243
245
|
*/
|
package/dist/utils/request.d.ts
CHANGED
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ export default class XmovAvatar {
|
|
|
43
43
|
|
|
44
44
|
// 使用 ! 操作符告诉 TypeScript 这些属性会在构造函数中被初始化
|
|
45
45
|
private resourceManager!: ResourceManager;
|
|
46
|
+
private beforeUnloadStopSessionPromise: ReturnType<ResourceManager["stopSession"]> | null = null;
|
|
46
47
|
private renderScheduler!: RenderScheduler;
|
|
47
48
|
private networkMonitor: NetworkMonitor | null = null;
|
|
48
49
|
private ttsa: Ttsa | null = null;
|
|
@@ -642,6 +643,9 @@ export default class XmovAvatar {
|
|
|
642
643
|
this.pendingInvisibleMode = true;
|
|
643
644
|
}
|
|
644
645
|
|
|
646
|
+
// Session 请求和资源下载期间也需要处理页面刷新。
|
|
647
|
+
window.addEventListener('beforeunload', this.handleBeforeUnload);
|
|
648
|
+
|
|
645
649
|
// 加载资源(sdk.resource_load:资源包下载/解析耗时)
|
|
646
650
|
(window as any).performanceTracker.markStart(performanceConstant.load_resource);
|
|
647
651
|
(window as any).performanceTracker.markStart(performanceConstant.first_avatar_render);
|
|
@@ -674,7 +678,6 @@ export default class XmovAvatar {
|
|
|
674
678
|
// 设置上报使用ttsa
|
|
675
679
|
(window as any).performanceTracker.setReportFunc(this.ttsa);
|
|
676
680
|
|
|
677
|
-
window.addEventListener('beforeunload', this.handleBeforeUnload);
|
|
678
681
|
document.addEventListener('visibilitychange', this.boundVisibilityChange);
|
|
679
682
|
initSpan?.end();
|
|
680
683
|
return sessionInfo;
|
|
@@ -1315,7 +1318,13 @@ export default class XmovAvatar {
|
|
|
1315
1318
|
}
|
|
1316
1319
|
|
|
1317
1320
|
private handleBeforeUnload = () => {
|
|
1318
|
-
this.
|
|
1321
|
+
if (!this._isClientMode && (this.resourceManager?.session_id || this.resourceManager?.session_request_id)) {
|
|
1322
|
+
// Page dismissal cannot wait for E2E and TTSA shutdown before starting DELETE.
|
|
1323
|
+
this.beforeUnloadStopSessionPromise ??= this.resourceManager.stopSession("beforeunload", { keepalive: true }).catch((error) => {
|
|
1324
|
+
(window as any).avatarSDKLogger.warn(this.TAG, "页面卸载时停止会话失败", error);
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
void this.destroy("beforeunload");
|
|
1319
1328
|
};
|
|
1320
1329
|
|
|
1321
1330
|
public isDestroyed() {
|
|
@@ -1459,9 +1468,10 @@ export default class XmovAvatar {
|
|
|
1459
1468
|
this.ttsa?.sendSdkPoint('close_session', {
|
|
1460
1469
|
reason: stop_reason,
|
|
1461
1470
|
});
|
|
1462
|
-
|
|
1471
|
+
// An unload DELETE is already in flight; neither await nor cancel it.
|
|
1472
|
+
const res = this.beforeUnloadStopSessionPromise ? undefined : await this.resourceManager.stopSession(stop_reason);
|
|
1463
1473
|
await this.destroyClient();
|
|
1464
|
-
if(res) {
|
|
1474
|
+
if(res || this.beforeUnloadStopSessionPromise) {
|
|
1465
1475
|
this.onStatusChange(AvatarStatus.close);
|
|
1466
1476
|
}
|
|
1467
1477
|
return res
|
|
@@ -1472,22 +1482,23 @@ export default class XmovAvatar {
|
|
|
1472
1482
|
try {
|
|
1473
1483
|
if (this._isClientMode) {
|
|
1474
1484
|
this._apiForwarder?.destroy(stopReason);
|
|
1475
|
-
} else if (!this.ttsa?.hasConnected) {
|
|
1476
|
-
// Initialization can own a session/request before TTSA first connects.
|
|
1477
|
-
// Mark an in-flight connection as quitting before waiting for DELETE.
|
|
1478
|
-
await this.ttsa?.quit(stopReason);
|
|
1479
|
-
await this.resourceManager.stopSession(stopReason);
|
|
1480
1485
|
} else {
|
|
1481
|
-
this.ttsa
|
|
1482
|
-
reason: stopReason
|
|
1483
|
-
}
|
|
1484
|
-
await this.ttsa
|
|
1486
|
+
if (this.ttsa?.hasConnected) {
|
|
1487
|
+
this.ttsa.sendSdkPoint('close_session', { reason: stopReason });
|
|
1488
|
+
}
|
|
1489
|
+
await this.ttsa?.quit(stopReason);
|
|
1485
1490
|
}
|
|
1486
1491
|
} catch (error) {
|
|
1487
1492
|
(window as any).avatarSDKLogger.warn(this.TAG, "TTSA client_quit 未确认,继续释放本地资源", error);
|
|
1488
1493
|
} finally {
|
|
1489
|
-
|
|
1490
|
-
|
|
1494
|
+
try {
|
|
1495
|
+
if (!this._isClientMode && !this.beforeUnloadStopSessionPromise) {
|
|
1496
|
+
await this.resourceManager.stopSession(stopReason);
|
|
1497
|
+
}
|
|
1498
|
+
} finally {
|
|
1499
|
+
await this.destroyClient();
|
|
1500
|
+
this.onStatusChange(AvatarStatus.close);
|
|
1501
|
+
}
|
|
1491
1502
|
}
|
|
1492
1503
|
}
|
|
1493
1504
|
|
|
@@ -10,6 +10,8 @@ import { headersNeedSign, generateRequestId } from "../utils/encodeToken";
|
|
|
10
10
|
// import { isDEV } from "../utils";
|
|
11
11
|
import CacheManager from './cache-manager';
|
|
12
12
|
|
|
13
|
+
const STOP_SESSION_TIMEOUT_MS = 3000;
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* 资源处理模块,加载并存储资源
|
|
15
17
|
*/
|
|
@@ -406,7 +408,8 @@ export default class ResourceManager {
|
|
|
406
408
|
});
|
|
407
409
|
}
|
|
408
410
|
|
|
409
|
-
async stopSession(stop_reason: string) {
|
|
411
|
+
async stopSession(stop_reason: string, options: { keepalive?: boolean } = {}) {
|
|
412
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
410
413
|
try {
|
|
411
414
|
const url = `${this.options.gatewayServer}`;
|
|
412
415
|
// session_id 尚未返回时(如 init 未完成就 destroy),只要有 request_id 仍发请求,
|
|
@@ -424,7 +427,19 @@ export default class ResourceManager {
|
|
|
424
427
|
data.request_id = this.session_request_id;
|
|
425
428
|
}
|
|
426
429
|
const { headers, data: signedData } = headersNeedSign(this.options.appId, this.options.appSecret, "DELETE", url, data);
|
|
427
|
-
|
|
430
|
+
// Unload requests must be allowed to outlive the document. Ordinary
|
|
431
|
+
// DELETEs have a deadline covering both response headers and body.
|
|
432
|
+
const controller = options.keepalive ? undefined : new AbortController();
|
|
433
|
+
if (controller) {
|
|
434
|
+
timeout = setTimeout(() => controller.abort(), STOP_SESSION_TIMEOUT_MS);
|
|
435
|
+
}
|
|
436
|
+
const response = await request(url, {
|
|
437
|
+
method: "DELETE",
|
|
438
|
+
data: signedData,
|
|
439
|
+
headers: { ...headers, ...this.options.headers },
|
|
440
|
+
keepalive: options.keepalive,
|
|
441
|
+
signal: controller?.signal,
|
|
442
|
+
});
|
|
428
443
|
return await response.json();
|
|
429
444
|
} catch (error) {
|
|
430
445
|
this.sdk.onMessage({
|
|
@@ -432,6 +447,8 @@ export default class ResourceManager {
|
|
|
432
447
|
message: `Error: 停止会话失败`,
|
|
433
448
|
e: error,
|
|
434
449
|
});
|
|
450
|
+
} finally {
|
|
451
|
+
if (timeout !== undefined) clearTimeout(timeout);
|
|
435
452
|
}
|
|
436
453
|
}
|
|
437
454
|
|
package/src/utils/request.ts
CHANGED
|
@@ -4,6 +4,8 @@ interface RequestOptions {
|
|
|
4
4
|
method?: HttpMethod
|
|
5
5
|
data?: Record<string, any>
|
|
6
6
|
headers?: Record<string, string>
|
|
7
|
+
keepalive?: boolean
|
|
8
|
+
signal?: AbortSignal
|
|
7
9
|
}
|
|
8
10
|
|
|
9
11
|
interface ErrorResponse {
|
|
@@ -21,6 +23,8 @@ export default async function request(
|
|
|
21
23
|
|
|
22
24
|
const config: RequestInit = {
|
|
23
25
|
method,
|
|
26
|
+
keepalive: options.keepalive,
|
|
27
|
+
signal: options.signal,
|
|
24
28
|
// headers,
|
|
25
29
|
headers: {
|
|
26
30
|
'Content-Type': 'application/json',
|
|
@@ -104,4 +108,4 @@ export function XMLRequest(options: {
|
|
|
104
108
|
};
|
|
105
109
|
xhr.send();
|
|
106
110
|
});
|
|
107
|
-
}
|
|
111
|
+
}
|