@xmov/avatar 2.4.0-alpha.4 → 2.4.0-alpha.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmov/avatar",
3
- "version": "2.4.0-alpha.4",
3
+ "version": "2.4.0-alpha.5",
4
4
  "description": "魔珐数字人 JS-SDK",
5
5
  "type": "module",
6
6
  "source": "src/index.ts",
package/src/index.ts CHANGED
@@ -133,6 +133,9 @@ export default class XmovAvatar {
133
133
  (window as any).avatarSDKLogger.setEnabled(options.enableLogger);
134
134
  this.options = options;
135
135
  this._env = options.env || "production"
136
+ // 提前创建 Sentry:无网络 / 容器不存在 / 能力检查失败等错误都要经 onMessage 上报,
137
+ // 而 onMessage 的上报条件是 sentryReporter 已存在,晚建会让这些早期错误全部丢失
138
+ this.initSentryReporter();
136
139
  this.enableClientInterrupt = options.enableClientInterrupt || false;
137
140
  this.boundVisibilityChange = this.visibilitychange.bind(this);
138
141
 
@@ -144,7 +147,14 @@ export default class XmovAvatar {
144
147
  }
145
148
 
146
149
  // 完整渲染模式(原有逻辑)
147
- this.initializeSDK(options);
150
+ // initializeSDK 是 fire-and-forget:内部同步抛错(如 layout.scale 校验)与未捕获的异步失败
151
+ // 都不会走 onMessage,这里兜底上报,避免业务既拿不到错误回调、监控也收不到
152
+ void this.initializeSDK(options).catch((e) => {
153
+ this.reportInitializationFailure(
154
+ `SDK 初始化失败: ${e?.message || e}`,
155
+ e
156
+ );
157
+ });
148
158
  this.networkMonitor = new NetworkMonitor({
149
159
  offlineCallback: (message) => {
150
160
  this.onMessage({
@@ -199,6 +209,34 @@ export default class XmovAvatar {
199
209
  })
200
210
  }
201
211
 
212
+ /**
213
+ * 创建 Sentry 错误上报实例(编译时常量 ENABLE_SENTRY + 运行时 sentry.enabled 双重控制)。
214
+ * 放在构造函数内完成:onMessage 的早期分支(无网络 / 容器不存在 / 能力检查失败)都发生在其后。
215
+ */
216
+ private initSentryReporter(): void {
217
+ if (!ENABLE_SENTRY || !this.options.sentry?.enabled) return;
218
+ try {
219
+ this.sentryReporter = new SentryReporter(SENTRY_DSN, {
220
+ tracesSampleRate: this.options.sentry?.tracesSampleRate,
221
+ });
222
+ this.sentryReporter.setTag('app_id', this.options.appId || '');
223
+ this.sentryReporter.setTag('env', this._env);
224
+ } catch (e) {
225
+ (window as any).avatarSDKLogger.warn(this.TAG, 'Sentry 初始化失败', e);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * 释放 Sentry 客户端(内部会 flush 未发送的事件)。
231
+ * 用于「实例确定不可用」的提前退出路径,避免业务反复重试创建时残留多个 client。
232
+ */
233
+ private disposeSentryReporter(): void {
234
+ if (!this.sentryReporter) return;
235
+ const reporter = this.sentryReporter;
236
+ this.sentryReporter = null;
237
+ void reporter.destroy().catch(() => {});
238
+ }
239
+
202
240
  public getStatus() {
203
241
  return this.status
204
242
  }
@@ -226,6 +264,8 @@ export default class XmovAvatar {
226
264
  code: EErrorCode.NETWORK_BREAK,
227
265
  message: '没有网络,请联网后重试',
228
266
  });
267
+ // 无网络直接退出,实例不会进入可用状态,释放 Sentry(内部会 flush 刚上报的事件)
268
+ this.disposeSentryReporter();
229
269
  return
230
270
  }
231
271
  const self = this;
@@ -247,6 +287,8 @@ export default class XmovAvatar {
247
287
  code: EErrorCode.CONTAINER_NOT_FOUND,
248
288
  message: `containerId: ${containerId} 不存在`,
249
289
  })
290
+ // 容器不存在,实例不会进入可用状态,释放 Sentry(内部会 flush 刚上报的事件)
291
+ this.disposeSentryReporter();
250
292
  return;
251
293
  }
252
294
  this.el = el;
@@ -416,7 +458,10 @@ export default class XmovAvatar {
416
458
  } else {
417
459
  const el = document.querySelector(containerId) as HTMLElement;
418
460
  if (!el) {
419
- this.options.onMessage?.({ code: EErrorCode.CONTAINER_NOT_FOUND, message: `containerId: ${containerId} 不存在` } as any);
461
+ // 走 onMessage 统一出口(而非直接调 options.onMessage),否则该错误不会进 Sentry
462
+ this.onMessage({ code: EErrorCode.CONTAINER_NOT_FOUND, message: `containerId: ${containerId} 不存在` });
463
+ // 容器不存在,实例不会进入可用状态,释放 Sentry(内部会 flush 刚上报的事件)
464
+ this.disposeSentryReporter();
420
465
  return;
421
466
  }
422
467
  this.el = el;
@@ -574,20 +619,8 @@ export default class XmovAvatar {
574
619
  return;
575
620
  }
576
621
 
577
- // 初始化 Sentry 错误监控(编译时常量 + 运行时配置双重控制)
578
- // 编译时 ENABLE_SENTRY 决定代码是否打包,运行时 sentry.enabled 决定是否启用
579
- // 提前到资源加载之前创建,使 init / resource_load span 可覆盖整个初始化过程
580
- if (ENABLE_SENTRY && this.options.sentry?.enabled) {
581
- try {
582
- this.sentryReporter = new SentryReporter(SENTRY_DSN, {
583
- tracesSampleRate: this.options.sentry?.tracesSampleRate,
584
- });
585
- this.sentryReporter.setTag('app_id', this.options.appId || '');
586
- this.sentryReporter.setTag('env', this._env);
587
- } catch (e) {
588
- (window as any).avatarSDKLogger.warn(this.TAG, 'Sentry 初始化失败', e);
589
- }
590
- }
622
+ // Sentry 实例已在构造函数中提前创建(见 initSentryReporter),
623
+ // 这样无网络 / 容器不存在 / 能力检查失败等更早的错误也能上报
591
624
  // sdk.init:初始化总耗时(含资源加载、连接就绪)
592
625
  const initSpan = this.sentryReporter?.startInactiveSpan('sdk.init', { op: 'init' });
593
626
  // sdk.first_frame:从初始化到首个 body 帧到达(handleMessage 首次 BODY 帧时结束)
@@ -1408,13 +1441,8 @@ export default class XmovAvatar {
1408
1441
  window.removeEventListener("beforeunload", this.handleBeforeUnload);
1409
1442
  document.removeEventListener("visibilitychange", this.boundVisibilityChange);
1410
1443
 
1411
- // 清理 Sentry 客户端
1412
- if (ENABLE_SENTRY && this.sentryReporter) {
1413
- try {
1414
- this.sentryReporter.destroy();
1415
- } catch {}
1416
- this.sentryReporter = null;
1417
- }
1444
+ // 清理 Sentry 客户端(内部会 flush 未发送的事件)
1445
+ this.disposeSentryReporter();
1418
1446
 
1419
1447
  (window as any).avatarSDKLogger.log(this.TAG, "SDK 已销毁");
1420
1448
  }