@spatius/avatarkit 1.3.0 → 1.3.1-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/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.1-beta.1] - 2026-07-16
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- The `region` configuration now defaults to automatic selection: when left unset, the SDK picks the closest serving region at initialization. Passing an explicit `region` continues to force that region, unchanged. If automatic selection can't be reached, the SDK falls back to a default region and continues initializing.
|
|
12
|
+
|
|
8
13
|
## [1.3.0] - 2026-07-04
|
|
9
14
|
|
|
10
15
|
### Added
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-
|
|
4
|
+
import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-C54v6mHz.js";
|
|
5
5
|
class StreamingAudioPlayer {
|
|
6
6
|
// Mark if AudioContext is being resumed, avoid concurrent resume requests
|
|
7
7
|
constructor(options) {
|
|
@@ -4189,6 +4189,7 @@ const RENDER_QUALITY_PARAMS = {
|
|
|
4189
4189
|
]: { renderScale: 1, splatRadius: 3 }
|
|
4190
4190
|
};
|
|
4191
4191
|
const DEFAULT_REGION = "us-west";
|
|
4192
|
+
const DEFAULT_REGION_REQUEST = "auto";
|
|
4192
4193
|
var LoadProgress = /* @__PURE__ */ ((LoadProgress2) => {
|
|
4193
4194
|
LoadProgress2["downloading"] = "downloading";
|
|
4194
4195
|
LoadProgress2["completed"] = "completed";
|
|
@@ -11586,11 +11587,259 @@ class EventStore {
|
|
|
11586
11587
|
}
|
|
11587
11588
|
}
|
|
11588
11589
|
const eventStore = new EventStore();
|
|
11590
|
+
const BOOTSTRAP_URL = "https://global.spatialwalk.top/bootstrap";
|
|
11591
|
+
async function fetchBootstrap(opts, signal, onResponse) {
|
|
11592
|
+
const res = await fetch(BOOTSTRAP_URL, {
|
|
11593
|
+
method: "POST",
|
|
11594
|
+
body: JSON.stringify({
|
|
11595
|
+
app_id: opts.appId,
|
|
11596
|
+
sdk_version: opts.sdkVersion,
|
|
11597
|
+
region: opts.region ?? "auto",
|
|
11598
|
+
platform: opts.platform ?? "web"
|
|
11599
|
+
}),
|
|
11600
|
+
cache: "no-store",
|
|
11601
|
+
signal
|
|
11602
|
+
});
|
|
11603
|
+
onResponse == null ? void 0 : onResponse();
|
|
11604
|
+
if (!res.ok) {
|
|
11605
|
+
throw new Error(`bootstrap HTTP ${res.status}`);
|
|
11606
|
+
}
|
|
11607
|
+
return await res.json();
|
|
11608
|
+
}
|
|
11609
|
+
const PROBE_TIMEOUT_MS = 5e3;
|
|
11610
|
+
const SAMPLE_COUNT = 3;
|
|
11611
|
+
const RECALIBRATE_INTERVAL_MS = 20 * 60 * 1e3;
|
|
11612
|
+
class ClockSync {
|
|
11613
|
+
constructor() {
|
|
11614
|
+
__publicField(this, "localBase", Date.now());
|
|
11615
|
+
__publicField(this, "serverBase", null);
|
|
11616
|
+
__publicField(this, "monoAtCalibrate", performance.now());
|
|
11617
|
+
/** 首次校准是否已落定(落定前埋点应挂队列) */
|
|
11618
|
+
__publicField(this, "calibrated", false);
|
|
11619
|
+
__publicField(this, "appId", "");
|
|
11620
|
+
__publicField(this, "sdkVersion", "");
|
|
11621
|
+
__publicField(this, "platform", "web");
|
|
11622
|
+
__publicField(this, "timer", null);
|
|
11623
|
+
__publicField(this, "visibilityHandler", null);
|
|
11624
|
+
__publicField(this, "syncing", false);
|
|
11625
|
+
/** 首次校准落定后触发的回调(供上报层 flush 挂起队列) */
|
|
11626
|
+
__publicField(this, "readyCallbacks", []);
|
|
11627
|
+
}
|
|
11628
|
+
/** 首次校准是否已完成(供上报闸门判断) */
|
|
11629
|
+
isReady() {
|
|
11630
|
+
return this.calibrated;
|
|
11631
|
+
}
|
|
11632
|
+
/**
|
|
11633
|
+
* 注册"首次校准落定"回调(成功或失败都算落定)。
|
|
11634
|
+
* 若已落定则立即同步触发一次。
|
|
11635
|
+
*/
|
|
11636
|
+
onReady(cb) {
|
|
11637
|
+
if (this.calibrated) {
|
|
11638
|
+
cb();
|
|
11639
|
+
return;
|
|
11640
|
+
}
|
|
11641
|
+
this.readyCallbacks.push(cb);
|
|
11642
|
+
}
|
|
11643
|
+
fireReady() {
|
|
11644
|
+
const cbs = this.readyCallbacks;
|
|
11645
|
+
this.readyCallbacks = [];
|
|
11646
|
+
for (const cb of cbs) {
|
|
11647
|
+
try {
|
|
11648
|
+
cb();
|
|
11649
|
+
} catch {
|
|
11650
|
+
}
|
|
11651
|
+
}
|
|
11652
|
+
}
|
|
11653
|
+
/** 本地时间戳:本地基准 + 单调流逝。一定有值。 */
|
|
11654
|
+
localNow() {
|
|
11655
|
+
return this.localAt(performance.now());
|
|
11656
|
+
}
|
|
11657
|
+
/** 后台时间戳:后台基准 + 单调流逝。未校准到后台时返回 null(上报留空)。 */
|
|
11658
|
+
serverNow() {
|
|
11659
|
+
return this.serverAt(performance.now());
|
|
11660
|
+
}
|
|
11661
|
+
/**
|
|
11662
|
+
* 仅供单测:直接设定基准,绕过真实网络校准。
|
|
11663
|
+
* @internal
|
|
11664
|
+
*/
|
|
11665
|
+
__setBaselineForTest(opts) {
|
|
11666
|
+
this.localBase = opts.localBase;
|
|
11667
|
+
this.serverBase = opts.serverBase;
|
|
11668
|
+
this.monoAtCalibrate = opts.monoAtCalibrate;
|
|
11669
|
+
this.calibrated = opts.calibrated ?? true;
|
|
11670
|
+
}
|
|
11671
|
+
/** 用"指定单调读数"算本地时间戳(供入队事件按入队时刻还原)。取整到毫秒。 */
|
|
11672
|
+
localAt(mono) {
|
|
11673
|
+
return Math.round(this.localBase + (mono - this.monoAtCalibrate));
|
|
11674
|
+
}
|
|
11675
|
+
/** 用"指定单调读数"算后台时间戳;未校准到后台时返回 null。取整到毫秒。 */
|
|
11676
|
+
serverAt(mono) {
|
|
11677
|
+
if (this.serverBase === null) return null;
|
|
11678
|
+
return Math.round(this.serverBase + (mono - this.monoAtCalibrate));
|
|
11679
|
+
}
|
|
11680
|
+
/**
|
|
11681
|
+
* 用时间戳做逻辑(端到端延迟 tap_N/anim_N 等)时用:优先后台时间戳,
|
|
11682
|
+
* 拿不到后台才退本地,保证与后台时刻同一时间线对齐。
|
|
11683
|
+
*/
|
|
11684
|
+
timelineNow() {
|
|
11685
|
+
return this.serverNow() ?? this.localNow();
|
|
11686
|
+
}
|
|
11687
|
+
/**
|
|
11688
|
+
* 把"采集时刻的单调读数"换算成时间线时刻(优先后台、退本地)。
|
|
11689
|
+
* 供标记解析用——采集时若校准未完成,先记单调读数,上报时再用最新基准换算。
|
|
11690
|
+
* @param mono 采集那一刻的 performance.now()
|
|
11691
|
+
*/
|
|
11692
|
+
resolveMono(mono) {
|
|
11693
|
+
const drift = mono - this.monoAtCalibrate;
|
|
11694
|
+
const base = this.serverBase ?? this.localBase;
|
|
11695
|
+
return Math.round(base + drift);
|
|
11696
|
+
}
|
|
11697
|
+
/**
|
|
11698
|
+
* 启动:立即校准一次,并注册定时 + 页面可见性重校准。
|
|
11699
|
+
* 幂等;fire-and-forget,不阻塞初始化。
|
|
11700
|
+
* @internal
|
|
11701
|
+
*/
|
|
11702
|
+
start(opts) {
|
|
11703
|
+
this.appId = opts.appId;
|
|
11704
|
+
this.sdkVersion = opts.sdkVersion;
|
|
11705
|
+
void this.calibrate();
|
|
11706
|
+
if (this.timer === null) {
|
|
11707
|
+
this.timer = setInterval(() => void this.calibrate(), RECALIBRATE_INTERVAL_MS);
|
|
11708
|
+
}
|
|
11709
|
+
if (this.visibilityHandler === null && typeof document !== "undefined") {
|
|
11710
|
+
this.visibilityHandler = () => {
|
|
11711
|
+
if (document.visibilityState === "visible") {
|
|
11712
|
+
void this.calibrate();
|
|
11713
|
+
}
|
|
11714
|
+
};
|
|
11715
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
11716
|
+
}
|
|
11717
|
+
}
|
|
11718
|
+
/**
|
|
11719
|
+
* 校准一次:多采样取最小 RTT 的样本,同时刷新本地/后台基准与单调零点。
|
|
11720
|
+
* - 拿到后台:serverBase 更新为估算值。
|
|
11721
|
+
* - 拿不到后台:serverBase 置空(后台时间戳留空),localBase 照常更新为当前墙钟。
|
|
11722
|
+
* 已校准过后若本次全失败:保留旧基准、跳过(不把好基准换成更差的)。
|
|
11723
|
+
* @internal
|
|
11724
|
+
*/
|
|
11725
|
+
async calibrate() {
|
|
11726
|
+
if (this.syncing) return;
|
|
11727
|
+
this.syncing = true;
|
|
11728
|
+
try {
|
|
11729
|
+
const samples = [];
|
|
11730
|
+
for (let i2 = 0; i2 < SAMPLE_COUNT; i2++) {
|
|
11731
|
+
const s2 = await this.probe();
|
|
11732
|
+
if (s2) samples.push(s2);
|
|
11733
|
+
}
|
|
11734
|
+
if (samples.length > 0) {
|
|
11735
|
+
const best = samples.reduce((a2, b2) => b2.rttNet < a2.rttNet ? b2 : a2);
|
|
11736
|
+
const firstCalibration = !this.calibrated;
|
|
11737
|
+
this.serverBase = best.serverBase;
|
|
11738
|
+
this.localBase = best.localBase;
|
|
11739
|
+
this.monoAtCalibrate = best.monoAtCalibrate;
|
|
11740
|
+
this.calibrated = true;
|
|
11741
|
+
if (firstCalibration) this.fireReady();
|
|
11742
|
+
logEvent("time_calibrated", "info", {
|
|
11743
|
+
has_server: true,
|
|
11744
|
+
rtt_ms: Math.round(best.rttNet)
|
|
11745
|
+
});
|
|
11746
|
+
return;
|
|
11747
|
+
}
|
|
11748
|
+
if (this.calibrated) {
|
|
11749
|
+
logger.log("[ClockSync] recalibrate got no server time, keeping previous baseline");
|
|
11750
|
+
return;
|
|
11751
|
+
}
|
|
11752
|
+
this.localBase = Date.now();
|
|
11753
|
+
this.monoAtCalibrate = performance.now();
|
|
11754
|
+
this.serverBase = null;
|
|
11755
|
+
this.calibrated = true;
|
|
11756
|
+
this.fireReady();
|
|
11757
|
+
logEvent("time_calibrated", "info", { has_server: false });
|
|
11758
|
+
} finally {
|
|
11759
|
+
this.syncing = false;
|
|
11760
|
+
}
|
|
11761
|
+
}
|
|
11762
|
+
/** 单次采样:请求 bootstrap,返回一组 (rttNet, serverBase, monoAtCalibrate, localBase);失败返回 null。 */
|
|
11763
|
+
async probe() {
|
|
11764
|
+
const controller = new AbortController();
|
|
11765
|
+
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
11766
|
+
try {
|
|
11767
|
+
let t1 = 0;
|
|
11768
|
+
let localAtT1 = 0;
|
|
11769
|
+
const t0 = performance.now();
|
|
11770
|
+
const body = await fetchBootstrap(
|
|
11771
|
+
{
|
|
11772
|
+
appId: this.appId,
|
|
11773
|
+
sdkVersion: this.sdkVersion,
|
|
11774
|
+
region: "auto",
|
|
11775
|
+
platform: this.platform
|
|
11776
|
+
},
|
|
11777
|
+
controller.signal,
|
|
11778
|
+
() => {
|
|
11779
|
+
t1 = performance.now();
|
|
11780
|
+
localAtT1 = Date.now();
|
|
11781
|
+
}
|
|
11782
|
+
);
|
|
11783
|
+
const ts2 = body == null ? void 0 : body.time_sync;
|
|
11784
|
+
if (!ts2 || typeof ts2.server_receive_ms !== "number" || typeof ts2.server_send_ms !== "number") {
|
|
11785
|
+
return null;
|
|
11786
|
+
}
|
|
11787
|
+
const rttNet = t1 - t0 - (ts2.server_send_ms - ts2.server_receive_ms);
|
|
11788
|
+
const serverBase = ts2.server_send_ms + rttNet / 2;
|
|
11789
|
+
return {
|
|
11790
|
+
rttNet: Math.max(0, rttNet),
|
|
11791
|
+
serverBase,
|
|
11792
|
+
monoAtCalibrate: t1,
|
|
11793
|
+
localBase: localAtT1
|
|
11794
|
+
};
|
|
11795
|
+
} catch {
|
|
11796
|
+
return null;
|
|
11797
|
+
} finally {
|
|
11798
|
+
clearTimeout(timer);
|
|
11799
|
+
}
|
|
11800
|
+
}
|
|
11801
|
+
/** 清理定时器与监听器。 */
|
|
11802
|
+
cleanup() {
|
|
11803
|
+
if (this.timer !== null) {
|
|
11804
|
+
clearInterval(this.timer);
|
|
11805
|
+
this.timer = null;
|
|
11806
|
+
}
|
|
11807
|
+
if (this.visibilityHandler !== null && typeof document !== "undefined") {
|
|
11808
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
11809
|
+
this.visibilityHandler = null;
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11813
|
+
const clockSync = new ClockSync();
|
|
11814
|
+
const MARK_KEY = "__mark";
|
|
11815
|
+
function monoTimestamp() {
|
|
11816
|
+
return { [MARK_KEY]: "mono", value: performance.now() };
|
|
11817
|
+
}
|
|
11818
|
+
function isMark(v2) {
|
|
11819
|
+
return typeof v2 === "object" && v2 !== null && MARK_KEY in v2;
|
|
11820
|
+
}
|
|
11821
|
+
function resolveMark(mark) {
|
|
11822
|
+
switch (mark[MARK_KEY]) {
|
|
11823
|
+
case "mono":
|
|
11824
|
+
return clockSync.resolveMono(mark.value);
|
|
11825
|
+
}
|
|
11826
|
+
}
|
|
11827
|
+
function resolveMarks(contents) {
|
|
11828
|
+
let out = null;
|
|
11829
|
+
for (const [k2, v2] of Object.entries(contents)) {
|
|
11830
|
+
if (isMark(v2)) {
|
|
11831
|
+
if (!out) out = { ...contents };
|
|
11832
|
+
out[k2] = resolveMark(v2);
|
|
11833
|
+
}
|
|
11834
|
+
}
|
|
11835
|
+
return out ?? contents;
|
|
11836
|
+
}
|
|
11589
11837
|
const OTEL_LOGGER_NAME = "spatius-avatarkit";
|
|
11590
11838
|
let sdkVersion$1 = "1.0.0";
|
|
11591
11839
|
let isInitialized$1 = false;
|
|
11592
11840
|
let loggerProvider = null;
|
|
11593
11841
|
const eventQueue$1 = [];
|
|
11842
|
+
const preCalibrationQueue = [];
|
|
11594
11843
|
function buildBasicAuthHeader() {
|
|
11595
11844
|
const credentials = `${OTEL_USERNAME}:${OTEL_PASSWORD}`;
|
|
11596
11845
|
return `Basic ${btoa(credentials)}`;
|
|
@@ -11644,6 +11893,7 @@ function initializeOtel(version, resourceAttrs) {
|
|
|
11644
11893
|
isInitialized$1 = true;
|
|
11645
11894
|
logger.log(`[OTel] Initialized successfully - endpoint: ${OTEL_LOGS_ENDPOINT}, stream: ${OTEL_STREAM_NAME}`);
|
|
11646
11895
|
flushEventQueue$1();
|
|
11896
|
+
clockSync.onReady(() => flushPreCalibrationQueue());
|
|
11647
11897
|
replayPersistedEvents().catch(() => {
|
|
11648
11898
|
});
|
|
11649
11899
|
} catch (error) {
|
|
@@ -11704,7 +11954,26 @@ function flushEventQueue$1() {
|
|
|
11704
11954
|
eventQueue$1.length = 0;
|
|
11705
11955
|
}
|
|
11706
11956
|
function trackEventOtel(event, level = "info", contents = {}) {
|
|
11707
|
-
|
|
11957
|
+
if (!clockSync.isReady()) {
|
|
11958
|
+
preCalibrationQueue.push({ event, level, contents, enqueueMono: performance.now() });
|
|
11959
|
+
return;
|
|
11960
|
+
}
|
|
11961
|
+
emitOtelEvent(event, level, contents, performance.now());
|
|
11962
|
+
}
|
|
11963
|
+
function flushPreCalibrationQueue() {
|
|
11964
|
+
if (preCalibrationQueue.length === 0) return;
|
|
11965
|
+
const pending = preCalibrationQueue.splice(0, preCalibrationQueue.length);
|
|
11966
|
+
for (const e2 of pending) {
|
|
11967
|
+
emitOtelEvent(e2.event, e2.level, e2.contents, e2.enqueueMono);
|
|
11968
|
+
}
|
|
11969
|
+
}
|
|
11970
|
+
function emitOtelEvent(event, level, contents, atMono) {
|
|
11971
|
+
contents = resolveMarks(contents);
|
|
11972
|
+
const timestamp = clockSync.localAt(atMono);
|
|
11973
|
+
const serverTs = clockSync.serverAt(atMono);
|
|
11974
|
+
if (serverTs !== null) {
|
|
11975
|
+
contents = { ...contents, server_timestamp: serverTs };
|
|
11976
|
+
}
|
|
11708
11977
|
eventStore.add({ event, level, contents, timestamp }).then((id) => {
|
|
11709
11978
|
const contentsWithIndex = { ...contents, _index: id };
|
|
11710
11979
|
if (!loggerProvider) return;
|
|
@@ -11807,6 +12076,15 @@ function cleanupOtel() {
|
|
|
11807
12076
|
loggerProvider = null;
|
|
11808
12077
|
}
|
|
11809
12078
|
}
|
|
12079
|
+
function timestampFields(enqueueMono) {
|
|
12080
|
+
const local = enqueueMono === void 0 ? clockSync.localNow() : clockSync.localAt(enqueueMono);
|
|
12081
|
+
const server = enqueueMono === void 0 ? clockSync.serverNow() : clockSync.serverAt(enqueueMono);
|
|
12082
|
+
const fields = { timestamp: local };
|
|
12083
|
+
if (server !== null) {
|
|
12084
|
+
fields.server_timestamp = server;
|
|
12085
|
+
}
|
|
12086
|
+
return fields;
|
|
12087
|
+
}
|
|
11810
12088
|
let sdkVersion = "1.0.0";
|
|
11811
12089
|
let isInitialized = false;
|
|
11812
12090
|
let commonRegion = "";
|
|
@@ -11885,6 +12163,7 @@ function initializePostHog(version, commonFields) {
|
|
|
11885
12163
|
}
|
|
11886
12164
|
posthogInstance.setPersonPropertiesForFlags(userProperties);
|
|
11887
12165
|
flushEventQueue();
|
|
12166
|
+
clockSync.onReady(() => flushEventQueue());
|
|
11888
12167
|
}
|
|
11889
12168
|
}, SDK_POSTHOG_INSTANCE_NAME);
|
|
11890
12169
|
} catch (error) {
|
|
@@ -11948,20 +12227,20 @@ function updatePostHogPersonPropertiesForFlags() {
|
|
|
11948
12227
|
instance.setPersonPropertiesForFlags(commonFields);
|
|
11949
12228
|
}
|
|
11950
12229
|
function flushEventQueue() {
|
|
11951
|
-
if (!sdkPosthogInstance || eventQueue.length === 0) {
|
|
12230
|
+
if (!sdkPosthogInstance || !clockSync.isReady() || eventQueue.length === 0) {
|
|
11952
12231
|
return;
|
|
11953
12232
|
}
|
|
11954
12233
|
logger.log(`[PostHog] Flushing ${eventQueue.length} queued events`);
|
|
11955
12234
|
setTimeout(() => {
|
|
11956
|
-
for (const { event, level, contents } of eventQueue) {
|
|
12235
|
+
for (const { event, level, contents, enqueueMono } of eventQueue) {
|
|
11957
12236
|
try {
|
|
11958
12237
|
const commonFields = getCommonFields();
|
|
11959
12238
|
const properties = {
|
|
11960
12239
|
...commonFields,
|
|
11961
12240
|
level,
|
|
11962
12241
|
service_module: "sdk",
|
|
11963
|
-
|
|
11964
|
-
...contents
|
|
12242
|
+
...timestampFields(enqueueMono),
|
|
12243
|
+
...resolveMarks(contents)
|
|
11965
12244
|
};
|
|
11966
12245
|
sdkPosthogInstance.capture(event, properties);
|
|
11967
12246
|
} catch (error) {
|
|
@@ -11977,8 +12256,8 @@ function trackEvent(event, level = "info", contents = {}) {
|
|
|
11977
12256
|
return;
|
|
11978
12257
|
}
|
|
11979
12258
|
const instance = getSdkPosthogInstance();
|
|
11980
|
-
if (!instance) {
|
|
11981
|
-
eventQueue.push({ event, level, contents });
|
|
12259
|
+
if (!instance || !clockSync.isReady()) {
|
|
12260
|
+
eventQueue.push({ event, level, contents, enqueueMono: performance.now() });
|
|
11982
12261
|
return;
|
|
11983
12262
|
}
|
|
11984
12263
|
sdkPosthogInstance = instance;
|
|
@@ -11988,8 +12267,8 @@ function trackEvent(event, level = "info", contents = {}) {
|
|
|
11988
12267
|
...commonFields,
|
|
11989
12268
|
level,
|
|
11990
12269
|
service_module: "sdk",
|
|
11991
|
-
|
|
11992
|
-
...contents
|
|
12270
|
+
...timestampFields(),
|
|
12271
|
+
...resolveMarks(contents)
|
|
11993
12272
|
};
|
|
11994
12273
|
sdkPosthogInstance.capture(event, properties);
|
|
11995
12274
|
} catch (error) {
|
|
@@ -12627,7 +12906,7 @@ const _AnimationPlayer = class _AnimationPlayer {
|
|
|
12627
12906
|
if (this.streamingPlayer) {
|
|
12628
12907
|
return;
|
|
12629
12908
|
}
|
|
12630
|
-
const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-
|
|
12909
|
+
const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-BQawYU25.js");
|
|
12631
12910
|
const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
|
|
12632
12911
|
const audioFormat = AvatarSDK2.getAudioFormat();
|
|
12633
12912
|
this.streamingPlayer = new StreamingAudioPlayer({
|
|
@@ -12885,6 +13164,38 @@ class AvatarViewRegistry {
|
|
|
12885
13164
|
}
|
|
12886
13165
|
}
|
|
12887
13166
|
const avatarViewRegistry = new AvatarViewRegistry();
|
|
13167
|
+
const RESOLVE_TIMEOUT_MS = 5e3;
|
|
13168
|
+
async function resolveRegion(opts) {
|
|
13169
|
+
var _a;
|
|
13170
|
+
const { appId, sdkVersion: sdkVersion2, requestedRegion } = opts;
|
|
13171
|
+
if (requestedRegion !== DEFAULT_REGION_REQUEST) {
|
|
13172
|
+
return requestedRegion;
|
|
13173
|
+
}
|
|
13174
|
+
const controller = new AbortController();
|
|
13175
|
+
const timer = setTimeout(() => controller.abort(), RESOLVE_TIMEOUT_MS);
|
|
13176
|
+
try {
|
|
13177
|
+
const res = await fetchBootstrap(
|
|
13178
|
+
{ appId, sdkVersion: sdkVersion2, region: "auto" },
|
|
13179
|
+
controller.signal
|
|
13180
|
+
);
|
|
13181
|
+
const current = (_a = res.region) == null ? void 0 : _a.current;
|
|
13182
|
+
if (typeof current === "string" && current.length > 0) {
|
|
13183
|
+
logger.log(`[RegionResolver] auto → ${current}`);
|
|
13184
|
+
return current;
|
|
13185
|
+
}
|
|
13186
|
+
throw new Error("bootstrap response missing region.current");
|
|
13187
|
+
} catch (error) {
|
|
13188
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13189
|
+
logger.warn(`[RegionResolver] auto resolve failed, falling back to ${DEFAULT_REGION}: ${message}`);
|
|
13190
|
+
logEvent("region_resolve_failed", "error", {
|
|
13191
|
+
fallback_region: DEFAULT_REGION,
|
|
13192
|
+
reason: message
|
|
13193
|
+
});
|
|
13194
|
+
return DEFAULT_REGION;
|
|
13195
|
+
} finally {
|
|
13196
|
+
clearTimeout(timer);
|
|
13197
|
+
}
|
|
13198
|
+
}
|
|
12888
13199
|
class AvatarCoreMemoryManager {
|
|
12889
13200
|
constructor(wasmModule) {
|
|
12890
13201
|
__publicField(this, "module");
|
|
@@ -14322,17 +14633,23 @@ class AvatarSDK {
|
|
|
14322
14633
|
}
|
|
14323
14634
|
static async _initializeInternal(appId, configuration) {
|
|
14324
14635
|
try {
|
|
14325
|
-
|
|
14636
|
+
const requestedRegion = configuration.region || DEFAULT_REGION_REQUEST;
|
|
14637
|
+
logger.log(`[AvatarSDK] Initializing with appId: ${appId}, region: ${requestedRegion}`);
|
|
14326
14638
|
resetDeprecationWarnings();
|
|
14327
|
-
this._configuration = configuration;
|
|
14328
14639
|
setLogLevel(configuration.logLevel ?? LogLevel.off);
|
|
14329
14640
|
idManager.setAppId(appId);
|
|
14330
14641
|
logger.log(`[AvatarSDK] Client ID: ${idManager.getClientId()}`);
|
|
14331
|
-
const region = configuration.region || DEFAULT_REGION;
|
|
14332
14642
|
const dsm = configuration.drivingServiceMode || DrivingServiceMode.direct;
|
|
14333
14643
|
const appIdValue = idManager.getAppId() || "";
|
|
14644
|
+
const region = await resolveRegion({
|
|
14645
|
+
appId: appIdValue,
|
|
14646
|
+
sdkVersion: this._version,
|
|
14647
|
+
requestedRegion
|
|
14648
|
+
});
|
|
14649
|
+
this._configuration = { ...configuration, region };
|
|
14334
14650
|
initializePostHog(this._version, { region, dsm });
|
|
14335
14651
|
initializeOtel(this._version, { region, dsm, appId: appIdValue });
|
|
14652
|
+
clockSync.start({ appId: appIdValue, sdkVersion: this._version });
|
|
14336
14653
|
await this.initializeWASMModule();
|
|
14337
14654
|
await this.initializeTemplateResources();
|
|
14338
14655
|
this._initializationState = "initialized";
|
|
@@ -14594,6 +14911,7 @@ class AvatarSDK {
|
|
|
14594
14911
|
idManager.clear();
|
|
14595
14912
|
cleanupPostHog();
|
|
14596
14913
|
cleanupOtel();
|
|
14914
|
+
clockSync.cleanup();
|
|
14597
14915
|
logger.log("[AvatarSDK] Cleanup completed");
|
|
14598
14916
|
} catch (error) {
|
|
14599
14917
|
logger.error("Failed to cleanup AvatarSDK:", error instanceof Error ? error.message : String(error));
|
|
@@ -14712,7 +15030,7 @@ class AvatarSDK {
|
|
|
14712
15030
|
__publicField(AvatarSDK, "_initializationState", "uninitialized");
|
|
14713
15031
|
__publicField(AvatarSDK, "_initializingPromise", null);
|
|
14714
15032
|
__publicField(AvatarSDK, "_configuration", null);
|
|
14715
|
-
__publicField(AvatarSDK, "_version", "1.3.
|
|
15033
|
+
__publicField(AvatarSDK, "_version", "1.3.1-beta.1");
|
|
14716
15034
|
__publicField(AvatarSDK, "_avatarCore", null);
|
|
14717
15035
|
__publicField(AvatarSDK, "_cachedDeviceScore", null);
|
|
14718
15036
|
__publicField(AvatarSDK, "_rendererBackend", null);
|
|
@@ -15338,14 +15656,14 @@ class NetworkLayer {
|
|
|
15338
15656
|
}
|
|
15339
15657
|
const metrics = this.audioMetrics;
|
|
15340
15658
|
if (!metrics.taps.has(0)) {
|
|
15341
|
-
metrics.taps.set(0,
|
|
15659
|
+
metrics.taps.set(0, monoTimestamp());
|
|
15342
15660
|
}
|
|
15343
15661
|
metrics.accumulatedBytes += audioData.byteLength;
|
|
15344
15662
|
const currentDuration = metrics.accumulatedBytes / this.getAudioBytesPerSecond();
|
|
15345
15663
|
const maxTapKey = metrics.taps.size > 0 ? Math.max(...metrics.taps.keys()) : 0;
|
|
15346
15664
|
let s2 = maxTapKey + 1;
|
|
15347
15665
|
while (currentDuration >= s2) {
|
|
15348
|
-
metrics.taps.set(s2,
|
|
15666
|
+
metrics.taps.set(s2, monoTimestamp());
|
|
15349
15667
|
s2++;
|
|
15350
15668
|
}
|
|
15351
15669
|
if (audioData.byteLength === 0 && !isLast) {
|
|
@@ -15502,7 +15820,7 @@ class NetworkLayer {
|
|
|
15502
15820
|
if (!this.dataController.getIsPlaying()) {
|
|
15503
15821
|
this.dataController.startStreamingPlayback();
|
|
15504
15822
|
}
|
|
15505
|
-
this.audioMetrics.anims.set(this.audioMetrics.animGroupCount,
|
|
15823
|
+
this.audioMetrics.anims.set(this.audioMetrics.animGroupCount, monoTimestamp());
|
|
15506
15824
|
this.audioMetrics.animGroupCount++;
|
|
15507
15825
|
} else {
|
|
15508
15826
|
logger.warn(`[NetworkLayer] Animation message has no animation data - conversationId: ${conversationId}`);
|
|
@@ -16336,15 +16654,15 @@ class AvatarController {
|
|
|
16336
16654
|
if (this.playbackMode === DrivingServiceMode.backend) {
|
|
16337
16655
|
const metrics = this.hostModeMetrics;
|
|
16338
16656
|
if (metrics.startTimestamp === 0) {
|
|
16339
|
-
metrics.startTimestamp =
|
|
16657
|
+
metrics.startTimestamp = clockSync.timelineNow();
|
|
16340
16658
|
}
|
|
16341
16659
|
metrics.accumulatedBytes += data.length;
|
|
16342
16660
|
const currentDuration = metrics.accumulatedBytes / this.audioBytesPerSecond;
|
|
16343
16661
|
if (currentDuration >= 1 && metrics.tap1Timestamp === 0) {
|
|
16344
|
-
metrics.tap1Timestamp =
|
|
16662
|
+
metrics.tap1Timestamp = clockSync.timelineNow();
|
|
16345
16663
|
}
|
|
16346
16664
|
if (currentDuration >= 2 && metrics.tap2Timestamp === 0) {
|
|
16347
|
-
metrics.tap2Timestamp =
|
|
16665
|
+
metrics.tap2Timestamp = clockSync.timelineNow();
|
|
16348
16666
|
}
|
|
16349
16667
|
}
|
|
16350
16668
|
if (this.isPlaying && ((_b = this.animationPlayer) == null ? void 0 : _b.isStreamingReady())) {
|
|
@@ -16465,7 +16783,7 @@ class AvatarController {
|
|
|
16465
16783
|
this.currentKeyframes = flameKeyframes;
|
|
16466
16784
|
if (this.playbackMode === DrivingServiceMode.backend && !this.hostModeMetrics.didRecvFirstFlame) {
|
|
16467
16785
|
this.hostModeMetrics.didRecvFirstFlame = true;
|
|
16468
|
-
this.hostModeMetrics.recvFirstFlameTimestamp =
|
|
16786
|
+
this.hostModeMetrics.recvFirstFlameTimestamp = clockSync.timelineNow();
|
|
16469
16787
|
}
|
|
16470
16788
|
} else {
|
|
16471
16789
|
this.currentKeyframes.push(...flameKeyframes);
|
|
@@ -20400,6 +20718,11 @@ class AvatarView {
|
|
|
20400
20718
|
* @internal
|
|
20401
20719
|
*/
|
|
20402
20720
|
createCanvas(container) {
|
|
20721
|
+
if (container.querySelector("canvas")) {
|
|
20722
|
+
logger.warn(
|
|
20723
|
+
"[AvatarView] The container already contains a <canvas>. This usually means another AvatarView was created in the same container without disposing the previous one, which will render two avatars side by side. Call dispose() on the previous AvatarView before creating a new one, or use a separate container."
|
|
20724
|
+
);
|
|
20725
|
+
}
|
|
20403
20726
|
const canvas = document.createElement("canvas");
|
|
20404
20727
|
const containerWidth = container.offsetWidth || 800;
|
|
20405
20728
|
const containerHeight = container.offsetHeight || 600;
|
|
@@ -21746,12 +22069,13 @@ export {
|
|
|
21746
22069
|
AvatarView as g,
|
|
21747
22070
|
RENDER_QUALITY_PARAMS as h,
|
|
21748
22071
|
DEFAULT_REGION as i,
|
|
21749
|
-
|
|
21750
|
-
|
|
22072
|
+
DEFAULT_REGION_REQUEST as j,
|
|
22073
|
+
LoadProgress as k,
|
|
21751
22074
|
logger as l,
|
|
21752
|
-
|
|
21753
|
-
|
|
21754
|
-
|
|
21755
|
-
|
|
21756
|
-
|
|
22075
|
+
AnimationType as m,
|
|
22076
|
+
ConversationState as n,
|
|
22077
|
+
AvatarState as o,
|
|
22078
|
+
AvatarError as p,
|
|
22079
|
+
ResourceType as q,
|
|
22080
|
+
extractResourceUrls as r
|
|
21757
22081
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { m, b, c, p, f, d, o, g, C, n, i, j, D, E, F, k, L, h, R, q, T, r } from "./index-C54v6mHz.js";
|
|
2
2
|
export {
|
|
3
|
-
|
|
3
|
+
m as AnimationType,
|
|
4
4
|
b as Avatar,
|
|
5
5
|
c as AvatarController,
|
|
6
|
-
|
|
6
|
+
p as AvatarError,
|
|
7
7
|
f as AvatarManager,
|
|
8
8
|
d as AvatarSDK,
|
|
9
|
-
|
|
9
|
+
o as AvatarState,
|
|
10
10
|
g as AvatarView,
|
|
11
11
|
C as ConnectionState,
|
|
12
|
-
|
|
12
|
+
n as ConversationState,
|
|
13
13
|
i as DEFAULT_REGION,
|
|
14
|
+
j as DEFAULT_REGION_REQUEST,
|
|
14
15
|
D as DrivingServiceMode,
|
|
15
16
|
E as ErrorCode,
|
|
16
17
|
F as FrameStarvationMode,
|
|
17
|
-
|
|
18
|
+
k as LoadProgress,
|
|
18
19
|
L as LogLevel,
|
|
19
20
|
h as RENDER_QUALITY_PARAMS,
|
|
20
21
|
R as RenderQuality,
|
|
21
|
-
|
|
22
|
+
q as ResourceType,
|
|
22
23
|
T as TransitionType,
|
|
23
|
-
|
|
24
|
+
r as extractResourceUrls
|
|
24
25
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spatius/avatarkit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.1-beta.1",
|
|
5
5
|
"packageManager": "pnpm@10.18.2",
|
|
6
6
|
"description": "AvatarKit SDK - Real-time Avatar Rendering SDK for Web",
|
|
7
7
|
"author": "AvatarKit Team",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"devDependencies": {
|
|
88
88
|
"@types/node": "^20.11.30",
|
|
89
89
|
"@webgpu/types": "^0.1.65",
|
|
90
|
+
"fflate": "0.8.3",
|
|
90
91
|
"tsx": "^4.20.6",
|
|
91
92
|
"typescript": "^5.0.0",
|
|
92
93
|
"vite": "^5.0.0",
|