@ziztechnology/dial-library 0.0.12 → 0.0.13

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/README.md CHANGED
@@ -225,9 +225,9 @@ controller.destroy();
225
225
 
226
226
  控制器默认每 200ms 读取一次,并保证上一次读取结束后才开始下一次。所有确认都使用传感器时间而不是轮询次数;相同 `sampledAtMs` 的重复数据不会推进窗口。
227
227
 
228
- 静止和匀速使用 accelerometer 的 1 秒三轴 MAD 稳健波动窗口,至少需要 5 个唯一样本。运动量不高于 `0.12 m/s²` 且陀螺仪足够稳定,连续 5 秒才进入 `STOPPED`;运动量达到 `0.18 m/s²` 或陀螺仪出现持续运动,连续 0.6 秒进入 `STEADY_DRIVING`。中间区间保持原基础状态。
228
+ 静止和匀速使用 accelerometer 的 1 秒三轴 MAD 稳健波动窗口,至少需要 5 个唯一样本。运动量不高于 `0.12 m/s²` 且陀螺仪足够稳定,连续 3 秒才进入 `STOPPED`;运动量达到 `0.18 m/s²` 或陀螺仪出现持续运动,连续 0.6 秒进入 `STEADY_DRIVING`。中间区间保持原基础状态。
229
229
 
230
- 普通加速和刹车要求 0.8 秒内至少 4/5 的样本方向一致;急加速和急刹要求峰值、0.6 秒持续均值及方向一致性同时成立,并拒绝垂直能量占主导或短时间反号的颠簸。转向由偏航持续值或累计转角触发并锁存到回正结束。最终优先级为:急加减速、已锁存转向、普通加减速、基础状态。
230
+ 普通加速和刹车要求 0.8 秒内至少 4/5 的样本方向一致;急加速和急刹要求峰值、0.6 秒持续均值及方向一致性同时成立,并拒绝垂直能量占主导或短时间反号的颠簸。车辆刚出现方向稳定的物理偏航后,强转向路径要求 0.4 秒内至少 3 个唯一样本,窗口均值达到 `0.08 rad/s`;在默认轮询频率下通常会在 0.4–0.6 秒内触发。转向也可以由累计转角触发,并锁存到回正结束。算法不会预测车辆尚未产生转动时的驾驶意图。最终优先级为:急加减速、已锁存转向、普通加减速、基础状态。
231
231
 
232
232
  缓慢转向还要求偏航旋转占陀螺仪能量的主要部分,或存在足够的横向加速度;垂直颠簸占主导且缺少偏航佐证时不会创建转向事件。重力样本短时缺失时,控制器会在 `maxSampleAgeMs` 内复用最后一个滤波后的车辆坐标系;缓存到期后按传感器不可用处理。单独调用 `classifyDrivingSnapshot()` 不会使用控制器缓存。
233
233
 
@@ -254,12 +254,14 @@ console.log(snapshot.tuningVersion);
254
254
  ```ts
255
255
  const controller = createDrivingStatusController({
256
256
  tuning: {
257
- turnYawThreshold: 0.13,
257
+ turnYawThreshold: 0.08,
258
+ turnConfirmationMs: 400,
259
+ turnMinimumSamples: 3,
258
260
  motionWindowMs: 1_200,
259
261
  motionMinimumSamples: 6,
260
262
  drivingMotionThreshold: 0.2,
261
263
  stoppedMotionThreshold: 0.11,
262
- stoppedConfirmationMs: 5_000,
264
+ stoppedConfirmationMs: 3_000,
263
265
  longitudinalThreshold: 0.75,
264
266
  turnYawDominanceThreshold: 0.65,
265
267
  turnLateralEvidenceThreshold: 0.3,
package/dist/index.d.mts CHANGED
@@ -413,6 +413,10 @@ interface DrivingExpressionPlayerSnapshot {
413
413
  }
414
414
  interface DrivingExpressionPlayerOptions {
415
415
  config: DrivingExpressionsConfigInput;
416
+ /**
417
+ * Emoji and image entries are renderable fallbacks. Other structured kinds
418
+ * remain accepted for source compatibility but are reported as invalid_config.
419
+ */
416
420
  fallbacks?: Partial<Readonly<Record<CarRunningStatus, StructuredDrivingExpressionMedia>>>;
417
421
  loadTimeoutMs?: number;
418
422
  retryBackoffMs?: number;
package/dist/index.mjs CHANGED
@@ -43,6 +43,45 @@ const dialSdkDebugWarn = (moduleName, scope, phase, data) => {
43
43
  } catch {}
44
44
  };
45
45
  //#endregion
46
+ //#region src/helper/sensor.ts
47
+ const extractFreshVector = (metric, capturedAtMs, maxSampleAgeMs) => {
48
+ if (!metric?.available || !Number.isFinite(capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) return null;
49
+ const ageMs = capturedAtMs - metric.sampledAtMs;
50
+ if (ageMs < 0 || ageMs > maxSampleAgeMs) return null;
51
+ const { x, y, z } = metric.value;
52
+ return [
53
+ x,
54
+ y,
55
+ z
56
+ ].every(Number.isFinite) ? {
57
+ sampledAtMs: metric.sampledAtMs,
58
+ value: metric.value
59
+ } : null;
60
+ };
61
+ //#endregion
62
+ //#region src/helper/validation.ts
63
+ const requirePositiveFinite = (name, value, debug) => {
64
+ dialSdkDebugWarn(debug.moduleName, debug.scope, "called", {
65
+ name,
66
+ value
67
+ });
68
+ if (!Number.isFinite(value) || value <= 0) {
69
+ const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
70
+ dialSdkDebugWarn(debug.moduleName, debug.scope, "threw", error);
71
+ throw error;
72
+ }
73
+ dialSdkDebugWarn(debug.moduleName, debug.scope, "returned", { result: value });
74
+ return value;
75
+ };
76
+ const isRecord = (value, debug) => {
77
+ const result = typeof value === "object" && value !== null && !Array.isArray(value);
78
+ if (debug) dialSdkDebugWarn(debug.moduleName, debug.scope, "returned", {
79
+ value,
80
+ result
81
+ });
82
+ return result;
83
+ };
84
+ //#endregion
46
85
  //#region src/sensor/check_status.ts
47
86
  const DEBUG_MODULE$5 = "check_status.ts";
48
87
  const DEVICE_RIGHT = {
@@ -56,7 +95,7 @@ const ZERO_VECTOR$1 = {
56
95
  z: 0
57
96
  };
58
97
  const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
59
- version: "driving-status-calibrated-2026-07",
98
+ version: "driving-status-calibrated-2026-07-17",
60
99
  maxSampleAgeMs: 1e3,
61
100
  gravityFilterTimeConstantMs: 1e3,
62
101
  biasFilterTimeConstantMs: 1e4,
@@ -69,7 +108,7 @@ const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
69
108
  stoppedRawGyroThreshold: .065,
70
109
  drivingGyroThreshold: .04,
71
110
  drivingRawGyroThreshold: .08,
72
- stoppedConfirmationMs: 5e3,
111
+ stoppedConfirmationMs: 3e3,
73
112
  drivingConfirmationMs: 600,
74
113
  longitudinalWindowMs: 800,
75
114
  longitudinalMinimumSamples: 5,
@@ -86,9 +125,9 @@ const DEFAULT_DRIVING_STATUS_TUNING = Object.freeze({
86
125
  verticalBumpRatio: 1.25,
87
126
  longitudinalReversalWindowMs: 400,
88
127
  turnWindowMs: 1500,
89
- turnMinimumSamples: 4,
90
- turnYawThreshold: .12,
91
- turnConfirmationMs: 600,
128
+ turnMinimumSamples: 3,
129
+ turnYawThreshold: .08,
130
+ turnConfirmationMs: 400,
92
131
  turnIntegratedAngleThreshold: .12,
93
132
  turnSignCoherence: .7,
94
133
  turnYawDominanceThreshold: .6,
@@ -164,7 +203,10 @@ const resolveDrivingStatusTuning = (overrides = {}) => {
164
203
  "turnExitYawThreshold",
165
204
  "turnExitConfirmationMs",
166
205
  "turnClosureWindowMs"
167
- ]) assertPositiveFinite(field, tuning[field]);
206
+ ]) requirePositiveFinite(field, tuning[field], {
207
+ moduleName: DEBUG_MODULE$5,
208
+ scope: "assertPositiveFinite"
209
+ });
168
210
  for (const field of [
169
211
  "motionMinimumSamples",
170
212
  "longitudinalMinimumSamples",
@@ -200,9 +242,9 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
200
242
  dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid capture time", { info });
201
243
  return null;
202
244
  }
203
- const linearAcceleration = availableFreshVector(info.linearAcceleration, info.capturedAtMs, tuning.maxSampleAgeMs);
204
- const gyroscope = availableFreshVector(info.gyroscope, info.capturedAtMs, tuning.maxSampleAgeMs);
205
- const gravityMetric = availableFreshVector(info.gravity, info.capturedAtMs, tuning.maxSampleAgeMs);
245
+ const linearAcceleration = extractFreshVector(info.linearAcceleration, info.capturedAtMs, tuning.maxSampleAgeMs);
246
+ const gyroscope = extractFreshVector(info.gyroscope, info.capturedAtMs, tuning.maxSampleAgeMs);
247
+ const gravityMetric = extractFreshVector(info.gravity, info.capturedAtMs, tuning.maxSampleAgeMs);
206
248
  const gravity = availableCorrectedGravity(correction, info.capturedAtMs, tuning.maxSampleAgeMs) ?? (correction.filteredGravity !== void 0 && gravityMetric !== null ? {
207
249
  value: correction.filteredGravity,
208
250
  sampledAtMs: gravityMetric.sampledAtMs
@@ -298,18 +340,6 @@ const classifyInstantaneousEvidence = (metrics, tuning) => {
298
340
  rejectionReason: null
299
341
  };
300
342
  };
301
- const availableFreshVector = (metric, capturedAtMs, maxSampleAgeMs) => {
302
- if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
303
- const { x, y, z } = metric.value;
304
- return [
305
- x,
306
- y,
307
- z
308
- ].every(Number.isFinite) ? {
309
- value: metric.value,
310
- sampledAtMs: metric.sampledAtMs
311
- } : null;
312
- };
313
343
  const availableCorrectedGravity = (correction, capturedAtMs, maxSampleAgeMs) => {
314
344
  const { filteredGravity, filteredGravitySampledAtMs } = correction;
315
345
  if (filteredGravity === void 0 || filteredGravitySampledAtMs === void 0 || !isFresh(filteredGravitySampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
@@ -349,13 +379,6 @@ const normalize = (value) => {
349
379
  const length = magnitude(value);
350
380
  return Number.isFinite(length) && length > Number.EPSILON ? scale(value, 1 / length) : null;
351
381
  };
352
- const assertPositiveFinite = (name, value) => {
353
- if (!Number.isFinite(value) || value <= 0) {
354
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
355
- dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "threw", error);
356
- throw error;
357
- }
358
- };
359
382
  const throwLoggedRangeError = (scope, message) => {
360
383
  const error = new RangeError(message);
361
384
  dialSdkDebugWarn(DEBUG_MODULE$5, scope, "threw", error);
@@ -421,8 +444,51 @@ const CAR_RUNNING_STATUS_PRIORITY = {
421
444
  SUDDEN_BRAKING: 5
422
445
  };
423
446
  //#endregion
447
+ //#region src/helper/async.ts
448
+ const createAbortError = (message, debug) => {
449
+ dialSdkDebugWarn(debug.moduleName, debug.scope, "called");
450
+ if (typeof DOMException === "function") {
451
+ const result = new DOMException(message, "AbortError");
452
+ dialSdkDebugWarn(debug.moduleName, debug.scope, "returned DOMException", { result });
453
+ return result;
454
+ }
455
+ const error = new Error(message);
456
+ error.name = "AbortError";
457
+ dialSdkDebugWarn(debug.moduleName, debug.scope, "returned Error", { result: error });
458
+ return error;
459
+ };
460
+ const isAbortError = (error, debug) => {
461
+ const result = error instanceof Error && error.name === "AbortError";
462
+ if (debug) dialSdkDebugWarn(debug.moduleName, debug.scope, "returned", {
463
+ error,
464
+ result
465
+ });
466
+ return result;
467
+ };
468
+ const invokeSafely = (callback, args, onError) => {
469
+ try {
470
+ callback(...args);
471
+ return true;
472
+ } catch (error) {
473
+ onError(error);
474
+ return false;
475
+ }
476
+ };
477
+ //#endregion
424
478
  //#region src/sensor/driving_expressions.ts
425
479
  const DEBUG_MODULE$3 = "driving_expressions.ts";
480
+ const IS_RECORD_DEBUG$1 = {
481
+ moduleName: DEBUG_MODULE$3,
482
+ scope: "isRecord"
483
+ };
484
+ const REQUIRE_POSITIVE_FINITE_DEBUG$1 = {
485
+ moduleName: DEBUG_MODULE$3,
486
+ scope: "requirePositiveFinite"
487
+ };
488
+ const CREATE_ABORT_ERROR_DEBUG$1 = {
489
+ moduleName: DEBUG_MODULE$3,
490
+ scope: "createAbortError"
491
+ };
426
492
  const OFFICIAL_DRIVING_EXPRESSION_FALLBACKS = Object.freeze({
427
493
  STOPPED: Object.freeze({
428
494
  kind: "emoji",
@@ -490,7 +556,7 @@ const parseDrivingExpressionsConfig = (raw, options = {}) => {
490
556
  options
491
557
  });
492
558
  const parsed = parseJsonObject(raw);
493
- if (parsed.schemaVersion !== 1 || !isRecord$1(parsed.states)) throw new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INVALID_SCHEMA", "drivingExpressions must use schemaVersion 1 and contain a states object");
559
+ if (parsed.schemaVersion !== 1 || !isRecord(parsed.states, IS_RECORD_DEBUG$1)) throw new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INVALID_SCHEMA", "drivingExpressions must use schemaVersion 1 and contain a states object");
494
560
  const knownStatuses = new Set(CAR_RUNNING_STATUSES);
495
561
  const stateKeys = Object.keys(parsed.states);
496
562
  if (stateKeys.length !== CAR_RUNNING_STATUSES.length || stateKeys.some((key) => !knownStatuses.has(key))) throw new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INCOMPLETE", "drivingExpressions.states must contain exactly the eight supported statuses");
@@ -516,6 +582,10 @@ const tryParseDrivingExpressionsConfig = (raw, options) => {
516
582
  dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "returned", { result });
517
583
  return result;
518
584
  } catch (error) {
585
+ if (!(error instanceof DrivingExpressionsConfigError)) {
586
+ dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "threw unexpected error", error);
587
+ throw error;
588
+ }
519
589
  dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "returned null after parse error", error);
520
590
  return null;
521
591
  }
@@ -544,7 +614,7 @@ const readDrivingExpressionsConfig = (raw, options) => {
544
614
  const waitForDrivingExpressionsConfig = (options = {}) => {
545
615
  dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig", "called", { options });
546
616
  const timeoutMs = requireNonNegativeFinite("timeoutMs", options.timeoutMs ?? 3e3);
547
- const pollIntervalMs = requirePositiveFinite$1("pollIntervalMs", options.pollIntervalMs ?? 100);
617
+ const pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 100, REQUIRE_POSITIVE_FINITE_DEBUG$1);
548
618
  const { signal } = options;
549
619
  return new Promise((resolve, reject) => {
550
620
  const deadlineMs = Date.now() + timeoutMs;
@@ -592,7 +662,7 @@ const waitForDrivingExpressionsConfig = (options = {}) => {
592
662
  };
593
663
  const handleAbort = () => {
594
664
  dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig.handleAbort", "called");
595
- fail(createAbortError$1());
665
+ fail(createAbortError("The operation was aborted", CREATE_ABORT_ERROR_DEBUG$1));
596
666
  };
597
667
  const read = () => {
598
668
  dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig.read", "called");
@@ -610,7 +680,13 @@ const waitForDrivingExpressionsConfig = (options = {}) => {
610
680
  handleAbort();
611
681
  return;
612
682
  }
613
- const config = read();
683
+ let config;
684
+ try {
685
+ config = read();
686
+ } catch (error) {
687
+ fail(error);
688
+ return;
689
+ }
614
690
  if (config !== null) {
615
691
  finish(config);
616
692
  return;
@@ -905,7 +981,7 @@ const parseDrivingExpressionMedia = (raw, status, options) => {
905
981
  dialSdkDebugWarn(DEBUG_MODULE$3, "parseDrivingExpressionMedia", "returned legacy image", { result });
906
982
  return result;
907
983
  }
908
- if (!isRecord$1(raw) || typeof raw.kind !== "string") throw invalidMedia(status, "media must be a structured object with a supported kind");
984
+ if (!isRecord(raw, IS_RECORD_DEBUG$1) || typeof raw.kind !== "string") throw invalidMedia(status, "media must be a structured object with a supported kind");
909
985
  const mediaAssetId = parseMediaAssetId(raw.mediaAssetId, status);
910
986
  assertFieldsAbsent(raw, status, [
911
987
  "items",
@@ -1011,7 +1087,7 @@ const parseJsonObject = (raw) => {
1011
1087
  });
1012
1088
  throw error;
1013
1089
  }
1014
- if (!isRecord$1(value)) {
1090
+ if (!isRecord(value, IS_RECORD_DEBUG$1)) {
1015
1091
  const error = new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INVALID_SCHEMA", "drivingExpressions must be a JSON object or JSON object string");
1016
1092
  dialSdkDebugWarn(DEBUG_MODULE$3, "parseJsonObject", "threw", error);
1017
1093
  throw error;
@@ -1028,7 +1104,7 @@ const readRuntimeDrivingExpressionsValue = () => {
1028
1104
  return result;
1029
1105
  }
1030
1106
  const faceConfig = runtimeGlobal.__TOOOONY_FACE_CONFIG__;
1031
- if (!isRecord$1(faceConfig)) {
1107
+ if (!isRecord(faceConfig, IS_RECORD_DEBUG$1)) {
1032
1108
  dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned", { result: void 0 });
1033
1109
  return;
1034
1110
  }
@@ -1037,7 +1113,7 @@ const readRuntimeDrivingExpressionsValue = () => {
1037
1113
  dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned face config", { result });
1038
1114
  return result;
1039
1115
  }
1040
- const result = isRecord$1(faceConfig.values) ? faceConfig.values.drivingExpressions : void 0;
1116
+ const result = isRecord(faceConfig.values, IS_RECORD_DEBUG$1) ? faceConfig.values.drivingExpressions : void 0;
1041
1117
  dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned legacy face config", { result });
1042
1118
  return result;
1043
1119
  };
@@ -1159,14 +1235,6 @@ const compact = (value) => {
1159
1235
  dialSdkDebugWarn(DEBUG_MODULE$3, "compact", "returned", { result: value });
1160
1236
  return value;
1161
1237
  };
1162
- const isRecord$1 = (value) => {
1163
- const result = typeof value === "object" && value !== null && !Array.isArray(value);
1164
- dialSdkDebugWarn(DEBUG_MODULE$3, "isRecord", "returned", {
1165
- value,
1166
- result
1167
- });
1168
- return result;
1169
- };
1170
1238
  const requireNonNegativeFinite = (name, value) => {
1171
1239
  dialSdkDebugWarn(DEBUG_MODULE$3, "requireNonNegativeFinite", "called", {
1172
1240
  name,
@@ -1180,31 +1248,6 @@ const requireNonNegativeFinite = (name, value) => {
1180
1248
  dialSdkDebugWarn(DEBUG_MODULE$3, "requireNonNegativeFinite", "returned", { result: value });
1181
1249
  return value;
1182
1250
  };
1183
- const requirePositiveFinite$1 = (name, value) => {
1184
- dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "called", {
1185
- name,
1186
- value
1187
- });
1188
- if (!Number.isFinite(value) || value <= 0) {
1189
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
1190
- dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "threw", error);
1191
- throw error;
1192
- }
1193
- dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "returned", { result: value });
1194
- return value;
1195
- };
1196
- const createAbortError$1 = () => {
1197
- dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "called");
1198
- if (typeof DOMException === "function") {
1199
- const result = new DOMException("The operation was aborted", "AbortError");
1200
- dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "returned DOMException", { result });
1201
- return result;
1202
- }
1203
- const error = /* @__PURE__ */ new Error("The operation was aborted");
1204
- error.name = "AbortError";
1205
- dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "returned Error", { result: error });
1206
- return error;
1207
- };
1208
1251
  //#endregion
1209
1252
  //#region src/sensor/page_lifecycle.ts
1210
1253
  const DEBUG_MODULE$2 = "page_lifecycle.ts";
@@ -1339,12 +1382,9 @@ const dispatchResume = () => {
1339
1382
  };
1340
1383
  const safeInvoke = (callback) => {
1341
1384
  dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "called");
1342
- try {
1343
- callback();
1344
- dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "returned");
1345
- } catch (error) {
1385
+ if (invokeSafely(callback, [], (error) => {
1346
1386
  dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "callback threw", error);
1347
- }
1387
+ })) dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "returned");
1348
1388
  };
1349
1389
  //#endregion
1350
1390
  //#region src/sensor/driving_status_controller.ts
@@ -1354,6 +1394,10 @@ const ZERO_VECTOR = {
1354
1394
  y: 0,
1355
1395
  z: 0
1356
1396
  };
1397
+ const REQUIRE_POSITIVE_FINITE_DEBUG = {
1398
+ moduleName: DEBUG_MODULE$1,
1399
+ scope: "requirePositiveFinite"
1400
+ };
1357
1401
  const createDrivingStatusController = (options = {}) => {
1358
1402
  dialSdkDebugWarn(DEBUG_MODULE$1, "createDrivingStatusController", "called", { options });
1359
1403
  const result = new DefaultDrivingStatusController(options);
@@ -1437,9 +1481,9 @@ var DefaultDrivingStatusController = class {
1437
1481
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "returned");
1438
1482
  };
1439
1483
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "called", { options });
1440
- this.sensorProvider = options.sensorProvider ?? defaultSensorProvider;
1441
- this.pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 200);
1442
- this.unavailableFallbackMs = requirePositiveFinite("unavailableFallbackMs", options.unavailableFallbackMs ?? 3e3);
1484
+ this.sensorProvider = options.sensorProvider ?? unifiedSensorInfo;
1485
+ this.pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 200, REQUIRE_POSITIVE_FINITE_DEBUG);
1486
+ this.unavailableFallbackMs = requirePositiveFinite("unavailableFallbackMs", options.unavailableFallbackMs ?? 3e3, REQUIRE_POSITIVE_FINITE_DEBUG);
1443
1487
  this.tuning = resolveDrivingStatusTuning(options.tuning);
1444
1488
  this.clock = options.clock ?? systemClock;
1445
1489
  this.onDiagnostic = options.onDiagnostic;
@@ -1610,7 +1654,7 @@ var DefaultDrivingStatusController = class {
1610
1654
  linearAccelerationBias: this.linearAccelerationBias,
1611
1655
  gyroscopeBias: this.gyroscopeBias
1612
1656
  });
1613
- const accelerometerSample = extractFreshVectorMetric(payload.accelerometer, payload.capturedAtMs, this.tuning.maxSampleAgeMs);
1657
+ const accelerometerSample = extractFreshVector(payload.accelerometer, payload.capturedAtMs, this.tuning.maxSampleAgeMs);
1614
1658
  if (gravity === null || metrics === null || accelerometerSample === null) {
1615
1659
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "invalid or stale snapshot", {
1616
1660
  metrics,
@@ -1622,7 +1666,7 @@ var DefaultDrivingStatusController = class {
1622
1666
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "processed sample", { capturedAtMs: metrics.capturedAtMs });
1623
1667
  }
1624
1668
  } catch (error) {
1625
- if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError$1(error)) {
1669
+ if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError(error)) {
1626
1670
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored provider error", { error });
1627
1671
  return;
1628
1672
  }
@@ -1638,7 +1682,7 @@ var DefaultDrivingStatusController = class {
1638
1682
  }
1639
1683
  }
1640
1684
  resolveGravityFrame(info) {
1641
- const sample = extractFreshVectorMetric(info.gravity, info.capturedAtMs, this.tuning.maxSampleAgeMs);
1685
+ const sample = extractFreshVector(info.gravity, info.capturedAtMs, this.tuning.maxSampleAgeMs);
1642
1686
  if (sample === null) {
1643
1687
  if (this.filteredGravity === null || this.lastGravitySampledAtMs === null) return null;
1644
1688
  const ageMs = info.capturedAtMs - this.lastGravitySampledAtMs;
@@ -1882,7 +1926,15 @@ var DefaultDrivingStatusController = class {
1882
1926
  });
1883
1927
  }
1884
1928
  commitSelectedStatus(now) {
1885
- const selected = this.urgentStatus ?? this.turnStatus ?? this.normalLongitudinalStatus ?? this.baseStatus;
1929
+ const selected = [
1930
+ this.baseStatus,
1931
+ this.normalLongitudinalStatus,
1932
+ this.turnStatus,
1933
+ this.urgentStatus
1934
+ ].reduce((highest, candidate) => {
1935
+ if (candidate === null) return highest;
1936
+ return CAR_RUNNING_STATUS_PRIORITY[candidate] > CAR_RUNNING_STATUS_PRIORITY[highest] ? candidate : highest;
1937
+ }, this.baseStatus);
1886
1938
  this.commit(selected, now, "candidate_confirmed");
1887
1939
  }
1888
1940
  processUnavailable(category) {
@@ -1940,7 +1992,12 @@ var DefaultDrivingStatusController = class {
1940
1992
  reason,
1941
1993
  atMs: now
1942
1994
  };
1943
- for (const listener of [...this.listeners]) safeCall(listener, event);
1995
+ for (const listener of [...this.listeners]) invokeSafely(listener, [event], (error) => {
1996
+ dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
1997
+ error,
1998
+ value: event
1999
+ });
2000
+ });
1944
2001
  this.emitDiagnostic({
1945
2002
  type: "status_committed",
1946
2003
  atMs: now,
@@ -2002,7 +2059,12 @@ var DefaultDrivingStatusController = class {
2002
2059
  event,
2003
2060
  hasListener: this.onDiagnostic !== void 0
2004
2061
  });
2005
- if (this.onDiagnostic) safeCall(this.onDiagnostic, event);
2062
+ if (this.onDiagnostic) invokeSafely(this.onDiagnostic, [event], (error) => {
2063
+ dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
2064
+ error,
2065
+ value: event
2066
+ });
2067
+ });
2006
2068
  }
2007
2069
  attachLifecycleListeners() {
2008
2070
  if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) return;
@@ -2187,7 +2249,6 @@ const median = (values) => {
2187
2249
  const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
2188
2250
  const latest = window[window.length - 1];
2189
2251
  if (latest && metrics[timestampKey] <= latest[timestampKey]) return false;
2190
- if (window.some((value) => value[timestampKey] === metrics[timestampKey])) return false;
2191
2252
  window.push(metrics);
2192
2253
  const cutoffMs = metrics[timestampKey] - windowMs;
2193
2254
  while (window[0] && window[0][timestampKey] < cutoffMs) window.shift();
@@ -2196,67 +2257,44 @@ const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
2196
2257
  const pushUniqueVector = (window, sample, windowMs) => {
2197
2258
  const latest = window[window.length - 1];
2198
2259
  if (latest && sample.sampledAtMs <= latest.sampledAtMs) return false;
2199
- if (window.some((value) => value.sampledAtMs === sample.sampledAtMs)) return false;
2200
2260
  window.push(sample);
2201
2261
  const cutoffMs = sample.sampledAtMs - windowMs;
2202
2262
  while (window[0] && window[0].sampledAtMs < cutoffMs) window.shift();
2203
2263
  return true;
2204
2264
  };
2205
- const extractFreshVectorMetric = (metric, capturedAtMs, maxSampleAgeMs) => {
2206
- if (!metric?.available || !Number.isFinite(capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) return null;
2207
- const ageMs = capturedAtMs - metric.sampledAtMs;
2208
- if (ageMs < 0 || ageMs > maxSampleAgeMs) return null;
2209
- const { x, y, z } = metric.value;
2210
- return [
2211
- x,
2212
- y,
2213
- z
2214
- ].every(Number.isFinite) ? {
2215
- sampledAtMs: metric.sampledAtMs,
2216
- value: metric.value
2217
- } : null;
2218
- };
2219
2265
  const observationTime = (metrics) => Math.max(metrics.linearAccelerationSampledAtMs, metrics.gyroscopeSampledAtMs);
2220
2266
  const lerpVector = (from, to, alpha) => ({
2221
2267
  x: from.x + (to.x - from.x) * alpha,
2222
2268
  y: from.y + (to.y - from.y) * alpha,
2223
2269
  z: from.z + (to.z - from.z) * alpha
2224
2270
  });
2225
- const defaultSensorProvider = () => {
2226
- const runtimeGlobal = globalThis;
2227
- if (typeof runtimeGlobal.unifiedSensorInfo !== "function") return Promise.reject(/* @__PURE__ */ new Error("window.unifiedSensorInfo is unavailable"));
2228
- return runtimeGlobal.unifiedSensorInfo();
2229
- };
2230
2271
  const systemClock = {
2231
2272
  now: () => Date.now(),
2232
2273
  setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
2233
2274
  clearTimeout: (handle) => globalThis.clearTimeout(handle)
2234
2275
  };
2235
2276
  const isBaseStatus = (status) => status === "STOPPED" || status === "STEADY_DRIVING";
2236
- const isAbortError$1 = (error) => error instanceof Error && error.name === "AbortError";
2237
- const requirePositiveFinite = (name, value) => {
2238
- if (!Number.isFinite(value) || value <= 0) {
2239
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
2240
- dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "threw", error);
2241
- throw error;
2242
- }
2243
- return value;
2244
- };
2245
- const safeCall = (callback, value) => {
2246
- try {
2247
- callback(value);
2248
- } catch (error) {
2249
- dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
2250
- error,
2251
- value
2252
- });
2253
- }
2254
- };
2255
2277
  const noop = () => void 0;
2256
2278
  //#endregion
2257
2279
  //#region src/sensor/driving_expression_player.ts
2258
2280
  const DEBUG_MODULE = "driving_expression_player.ts";
2259
2281
  const MAX_RETAINED_IMAGES = 3;
2282
+ const POSITIVE_FINITE_DEBUG = {
2283
+ moduleName: DEBUG_MODULE,
2284
+ scope: "positiveFinite"
2285
+ };
2286
+ const CREATE_ABORT_ERROR_DEBUG = {
2287
+ moduleName: DEBUG_MODULE,
2288
+ scope: "createAbortError"
2289
+ };
2290
+ const IS_ABORT_ERROR_DEBUG = {
2291
+ moduleName: DEBUG_MODULE,
2292
+ scope: "isAbortError"
2293
+ };
2294
+ const IS_RECORD_DEBUG = {
2295
+ moduleName: DEBUG_MODULE,
2296
+ scope: "isRecord"
2297
+ };
2260
2298
  const createDrivingExpressionPlayer = (container, options) => {
2261
2299
  dialSdkDebugWarn(DEBUG_MODULE, "createDrivingExpressionPlayer", "called", {
2262
2300
  container,
@@ -2273,6 +2311,7 @@ var DefaultDrivingExpressionPlayer = class {
2273
2311
  this.mediaKind = null;
2274
2312
  this.errorCategory = null;
2275
2313
  this.desiredMedia = null;
2314
+ this.desiredMediaRole = null;
2276
2315
  this.userPaused = false;
2277
2316
  this.lifecycleSuspended = false;
2278
2317
  this.paused = false;
@@ -2321,15 +2360,15 @@ var DefaultDrivingExpressionPlayer = class {
2321
2360
  this.document = container.ownerDocument;
2322
2361
  this.config = options.config;
2323
2362
  this.fallbacks = options.fallbacks;
2324
- this.loadTimeoutMs = positiveFinite("loadTimeoutMs", options.loadTimeoutMs ?? 5e3);
2325
- this.retryBackoffMs = positiveFinite("retryBackoffMs", options.retryBackoffMs ?? 15e3);
2363
+ this.loadTimeoutMs = requirePositiveFinite("loadTimeoutMs", options.loadTimeoutMs ?? 5e3, POSITIVE_FINITE_DEBUG);
2364
+ this.retryBackoffMs = requirePositiveFinite("retryBackoffMs", options.retryBackoffMs ?? 15e3, POSITIVE_FINITE_DEBUG);
2326
2365
  this.fit = options.fit ?? "contain";
2327
2366
  this.tgsRenderer = defaultTgsRenderer;
2328
2367
  this.fetchImplementation = options.fetch ?? globalThis.fetch?.bind(globalThis);
2329
2368
  this.onDiagnostic = options.onDiagnostic;
2330
- this.maxTgsCompressedBytes = positiveFinite("maxTgsCompressedBytes", options.maxTgsCompressedBytes ?? 2 * 1024 * 1024);
2331
- this.maxTgsJsonBytes = positiveFinite("maxTgsJsonBytes", options.maxTgsJsonBytes ?? 8 * 1024 * 1024);
2332
- this.maxTgsLayers = positiveFinite("maxTgsLayers", options.maxTgsLayers ?? 500);
2369
+ this.maxTgsCompressedBytes = requirePositiveFinite("maxTgsCompressedBytes", options.maxTgsCompressedBytes ?? 2 * 1024 * 1024, POSITIVE_FINITE_DEBUG);
2370
+ this.maxTgsJsonBytes = requirePositiveFinite("maxTgsJsonBytes", options.maxTgsJsonBytes ?? 8 * 1024 * 1024, POSITIVE_FINITE_DEBUG);
2371
+ this.maxTgsLayers = requirePositiveFinite("maxTgsLayers", options.maxTgsLayers ?? 500, POSITIVE_FINITE_DEBUG);
2333
2372
  if (options.managePageLifecycle ?? true) this.unsubscribePageLifecycle = subscribeDrivingPageLifecycle(this.handleLifecyclePause, this.handleLifecycleResume);
2334
2373
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.constructor", "created", {
2335
2374
  loadTimeoutMs: this.loadTimeoutMs,
@@ -2354,24 +2393,33 @@ var DefaultDrivingExpressionPlayer = class {
2354
2393
  const operation = this.beginOperation();
2355
2394
  const configured = this.config.states[status];
2356
2395
  if (configured === void 0) {
2357
- const fallback = this.fallbacks?.[status];
2358
- if (fallback === void 0) {
2396
+ const fallback = this.resolveFallback(status);
2397
+ if (fallback.type === "missing") {
2398
+ this.desiredMedia = null;
2399
+ this.desiredMediaRole = null;
2359
2400
  this.handleFailure(status, null, new DrivingExpressionResolutionError(status), "invalid_config", operation, false);
2360
2401
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned after missing config");
2361
2402
  return;
2362
2403
  }
2363
- this.desiredMedia = fallback;
2404
+ this.desiredMedia = fallback.media;
2405
+ this.desiredMediaRole = "fallback";
2364
2406
  if (this.paused) {
2365
2407
  this.phase = "idle";
2366
2408
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "deferred fallback while paused");
2367
2409
  return;
2368
2410
  }
2369
- this.renderFallback(status, fallback);
2411
+ if (fallback.type === "unsupported") {
2412
+ this.handleFailure(status, fallback.media, unsupportedFallbackError(fallback.media), "invalid_config", operation, false, false);
2413
+ dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned unsupported fallback");
2414
+ return;
2415
+ }
2416
+ this.renderFallback(status, fallback.media);
2370
2417
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned fallback", { snapshot: this.getSnapshot() });
2371
2418
  return;
2372
2419
  }
2373
2420
  const media = normalizeMedia(configured);
2374
2421
  this.desiredMedia = media;
2422
+ this.desiredMediaRole = "primary";
2375
2423
  if (this.paused) {
2376
2424
  this.phase = "idle";
2377
2425
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "deferred media while paused", { media });
@@ -2422,6 +2470,7 @@ var DefaultDrivingExpressionPlayer = class {
2422
2470
  this.container.replaceChildren();
2423
2471
  this.status = null;
2424
2472
  this.desiredMedia = null;
2473
+ this.desiredMediaRole = null;
2425
2474
  this.phase = "idle";
2426
2475
  this.mediaKind = null;
2427
2476
  this.errorCategory = null;
@@ -2490,7 +2539,12 @@ var DefaultDrivingExpressionPlayer = class {
2490
2539
  }
2491
2540
  if (this.status !== null && this.desiredMedia !== null) {
2492
2541
  const operation = this.beginOperation();
2493
- this.load(this.status, this.desiredMedia, operation);
2542
+ if (this.desiredMediaRole === "fallback") {
2543
+ const status = this.status;
2544
+ const fallback = this.desiredMedia;
2545
+ if (isStaticFallback(fallback)) this.renderFallback(status, fallback);
2546
+ else this.handleFailure(status, fallback, unsupportedFallbackError(fallback), "invalid_config", operation, false, false);
2547
+ } else this.load(this.status, this.desiredMedia, operation);
2494
2548
  }
2495
2549
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.updatePauseState", "returned", this.getSnapshot());
2496
2550
  }
@@ -2553,7 +2607,7 @@ var DefaultDrivingExpressionPlayer = class {
2553
2607
  error,
2554
2608
  operation
2555
2609
  });
2556
- if (!this.isCurrent(operation) || isAbortError(error)) {
2610
+ if (!this.isCurrent(operation) || isAbortError(error, IS_ABORT_ERROR_DEBUG)) {
2557
2611
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.load", "ignored error");
2558
2612
  return;
2559
2613
  }
@@ -2774,19 +2828,37 @@ var DefaultDrivingExpressionPlayer = class {
2774
2828
  status,
2775
2829
  report
2776
2830
  });
2777
- const fallback = this.fallbacks?.[status];
2778
- if (!fallback) {
2831
+ const fallback = this.resolveFallback(status);
2832
+ if (fallback.type !== "static") {
2779
2833
  this.container.replaceChildren();
2780
- dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", { result: false });
2834
+ dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
2835
+ result: false,
2836
+ fallbackType: fallback.type
2837
+ });
2781
2838
  return false;
2782
2839
  }
2783
- this.renderFallback(status, fallback, report);
2840
+ this.renderFallback(status, fallback.media, report);
2784
2841
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
2785
2842
  result: true,
2786
- fallback
2843
+ fallback: fallback.media
2787
2844
  });
2788
2845
  return true;
2789
2846
  }
2847
+ resolveFallback(status) {
2848
+ const fallback = this.fallbacks?.[status];
2849
+ const result = fallback === void 0 ? { type: "missing" } : isStaticFallback(fallback) ? {
2850
+ type: "static",
2851
+ media: fallback
2852
+ } : {
2853
+ type: "unsupported",
2854
+ media: fallback
2855
+ };
2856
+ dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.resolveFallback", "returned", {
2857
+ status,
2858
+ result
2859
+ });
2860
+ return result;
2861
+ }
2790
2862
  renderFallback(status, fallback, report = true) {
2791
2863
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderFallback", "called", {
2792
2864
  status,
@@ -2799,11 +2871,11 @@ var DefaultDrivingExpressionPlayer = class {
2799
2871
  this.errorCategory = null;
2800
2872
  }
2801
2873
  if (fallback.kind === "emoji") this.renderEmoji(fallback.text);
2802
- else if (fallback.kind === "image") {
2874
+ else {
2803
2875
  const image = this.createImage();
2804
2876
  image.src = fallback.url;
2805
2877
  this.container.replaceChildren(image);
2806
- } else this.container.replaceChildren();
2878
+ }
2807
2879
  if (report) this.emitDiagnostic({
2808
2880
  type: "media_fallback_applied",
2809
2881
  status,
@@ -2852,14 +2924,15 @@ var DefaultDrivingExpressionPlayer = class {
2852
2924
  });
2853
2925
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.markReady", "returned", this.getSnapshot());
2854
2926
  }
2855
- handleFailure(status, media, error, category, operation, scheduleRetry = true) {
2927
+ handleFailure(status, media, error, category, operation, scheduleRetry = true, applyFallback = true) {
2856
2928
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.handleFailure", "called", {
2857
2929
  status,
2858
2930
  media,
2859
2931
  error,
2860
2932
  category,
2861
2933
  operation,
2862
- scheduleRetry
2934
+ scheduleRetry,
2935
+ applyFallback
2863
2936
  });
2864
2937
  if (!this.isCurrent(operation) || this.failedOperation === operation) {
2865
2938
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.handleFailure", "ignored");
@@ -2868,6 +2941,7 @@ var DefaultDrivingExpressionPlayer = class {
2868
2941
  this.failedOperation = operation;
2869
2942
  this.stopActiveVideo();
2870
2943
  this.phase = "error";
2944
+ if (!applyFallback && media !== null) this.mediaKind = media.kind;
2871
2945
  this.errorCategory = category;
2872
2946
  this.emitDiagnostic({
2873
2947
  type: "media_load_failed",
@@ -2876,8 +2950,24 @@ var DefaultDrivingExpressionPlayer = class {
2876
2950
  category,
2877
2951
  message: safeErrorMessage(error)
2878
2952
  });
2879
- const fallbackApplied = this.renderConfiguredFallback(status);
2880
- if (!fallbackApplied) {
2953
+ let fallbackApplied = false;
2954
+ const fallback = applyFallback ? this.resolveFallback(status) : { type: "missing" };
2955
+ if (fallback.type === "static") {
2956
+ this.renderFallback(status, fallback.media);
2957
+ fallbackApplied = true;
2958
+ } else if (fallback.type === "unsupported") {
2959
+ this.container.replaceChildren();
2960
+ this.phase = "error";
2961
+ this.mediaKind = fallback.media.kind;
2962
+ this.errorCategory = "invalid_config";
2963
+ this.emitDiagnostic({
2964
+ type: "media_load_failed",
2965
+ status,
2966
+ kind: fallback.media.kind,
2967
+ category: "invalid_config",
2968
+ message: safeErrorMessage(unsupportedFallbackError(fallback.media))
2969
+ });
2970
+ } else {
2881
2971
  this.phase = "error";
2882
2972
  this.errorCategory = category;
2883
2973
  }
@@ -3123,12 +3213,9 @@ var DefaultDrivingExpressionPlayer = class {
3123
3213
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned no listener");
3124
3214
  return;
3125
3215
  }
3126
- try {
3127
- this.onDiagnostic(event);
3128
- dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned", { event });
3129
- } catch (error) {
3216
+ if (invokeSafely(this.onDiagnostic, [event], (error) => {
3130
3217
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "listener threw", error);
3131
- }
3218
+ })) dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned", { event });
3132
3219
  }
3133
3220
  };
3134
3221
  var MediaPlayerError = class extends Error {
@@ -3161,6 +3248,8 @@ const normalizeMedia = (media) => {
3161
3248
  });
3162
3249
  return result;
3163
3250
  };
3251
+ const isStaticFallback = (media) => media.kind === "emoji" || media.kind === "image";
3252
+ const unsupportedFallbackError = (media) => new MediaPlayerError("invalid_config", `Fallback media kind ${media.kind} is unsupported; use emoji or image`);
3164
3253
  const isRetainableImage = (media) => {
3165
3254
  if (media.mimeType?.split(";", 1)[0]?.trim().toLowerCase() === "image/gif") return false;
3166
3255
  try {
@@ -3246,7 +3335,7 @@ const waitForElementEvent = (element, successEvent, failureEvent, signal, timeou
3246
3335
  const handleAbort = () => {
3247
3336
  dialSdkDebugWarn(DEBUG_MODULE, "waitForElementEvent.handleAbort", "called");
3248
3337
  cleanup();
3249
- reject(createAbortError());
3338
+ reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
3250
3339
  };
3251
3340
  element.addEventListener(successEvent, handleSuccess, { once: true });
3252
3341
  element.addEventListener(failureEvent, handleFailure, { once: true });
@@ -3283,7 +3372,7 @@ const withTimeout = (promise, timeoutMs, signal) => {
3283
3372
  const handleAbort = () => {
3284
3373
  dialSdkDebugWarn(DEBUG_MODULE, "withTimeout.handleAbort", "called");
3285
3374
  cleanup();
3286
- reject(createAbortError());
3375
+ reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
3287
3376
  };
3288
3377
  signal.addEventListener("abort", handleAbort, { once: true });
3289
3378
  timer = globalThis.setTimeout(() => {
@@ -3310,9 +3399,9 @@ const defaultTgsRenderer = async (container, animationData, signal) => {
3310
3399
  animationData,
3311
3400
  signal
3312
3401
  });
3313
- if (signal.aborted) throw createAbortError();
3402
+ if (signal.aborted) throw createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG);
3314
3403
  const module = await import("lottie-web/build/player/lottie_light.js");
3315
- if (signal.aborted) throw createAbortError();
3404
+ if (signal.aborted) throw createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG);
3316
3405
  const animation = module.default.loadAnimation({
3317
3406
  animationData,
3318
3407
  autoplay: true,
@@ -3336,7 +3425,7 @@ const defaultTgsRenderer = async (container, animationData, signal) => {
3336
3425
  dialSdkDebugWarn(DEBUG_MODULE, "defaultTgsRenderer.aborted", "called");
3337
3426
  cleanup();
3338
3427
  animation.destroy();
3339
- reject(createAbortError());
3428
+ reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
3340
3429
  };
3341
3430
  const cleanup = () => {
3342
3431
  dialSdkDebugWarn(DEBUG_MODULE, "defaultTgsRenderer.cleanup", "called");
@@ -3375,7 +3464,7 @@ function validateTgsAnimation(value, maxLayers) {
3375
3464
  value,
3376
3465
  maxLayers
3377
3466
  });
3378
- if (!isRecord(value) || !Array.isArray(value.layers)) throw new MediaPlayerError("decode", "TGS animation does not contain a layers array");
3467
+ if (!isRecord(value, IS_RECORD_DEBUG) || !Array.isArray(value.layers)) throw new MediaPlayerError("decode", "TGS animation does not contain a layers array");
3379
3468
  if (value.layers.length > maxLayers) throw new MediaPlayerError("decode", "TGS animation has too many layers");
3380
3469
  if (typeof value.fr === "number" && (!Number.isFinite(value.fr) || value.fr <= 0 || value.fr > 120)) throw new MediaPlayerError("decode", "TGS animation frame rate is outside the supported range");
3381
3470
  if (typeof value.ip === "number" && typeof value.op === "number" && (value.op <= value.ip || value.op - value.ip > 3e4)) throw new MediaPlayerError("decode", "TGS animation duration is outside the supported range");
@@ -3430,11 +3519,7 @@ const resetVideo = (video) => {
3430
3519
  video.pause();
3431
3520
  video.removeAttribute("src");
3432
3521
  video.removeAttribute("type");
3433
- try {
3434
- video.load();
3435
- } catch (error) {
3436
- dialSdkDebugWarn(DEBUG_MODULE, "resetVideo", "load threw", error);
3437
- }
3522
+ video.load();
3438
3523
  dialSdkDebugWarn(DEBUG_MODULE, "resetVideo", "returned", { video });
3439
3524
  };
3440
3525
  const styleMediaElement = (element, fit) => {
@@ -3481,39 +3566,6 @@ const redactUrlQueries = (message) => {
3481
3566
  dialSdkDebugWarn(DEBUG_MODULE, "redactUrlQueries", "returned", { result });
3482
3567
  return result;
3483
3568
  };
3484
- const createAbortError = () => {
3485
- dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "called");
3486
- if (typeof DOMException !== "undefined") {
3487
- const result = new DOMException("Aborted", "AbortError");
3488
- dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "returned DOMException", { result });
3489
- return result;
3490
- }
3491
- const error = /* @__PURE__ */ new Error("Aborted");
3492
- error.name = "AbortError";
3493
- dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "returned Error", { result: error });
3494
- return error;
3495
- };
3496
- const isAbortError = (error) => {
3497
- const result = error instanceof Error && error.name === "AbortError";
3498
- dialSdkDebugWarn(DEBUG_MODULE, "isAbortError", "returned", {
3499
- error,
3500
- result
3501
- });
3502
- return result;
3503
- };
3504
- const positiveFinite = (name, value) => {
3505
- dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "called", {
3506
- name,
3507
- value
3508
- });
3509
- if (!Number.isFinite(value) || value <= 0) {
3510
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
3511
- dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "threw", error);
3512
- throw error;
3513
- }
3514
- dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "returned", { result: value });
3515
- return value;
3516
- };
3517
3569
  const safeDestroy = (handle) => {
3518
3570
  dialSdkDebugWarn(DEBUG_MODULE, "safeDestroy", "called", { handle });
3519
3571
  try {
@@ -3523,13 +3575,5 @@ const safeDestroy = (handle) => {
3523
3575
  dialSdkDebugWarn(DEBUG_MODULE, "safeDestroy", "destroy threw", error);
3524
3576
  }
3525
3577
  };
3526
- const isRecord = (value) => {
3527
- const result = typeof value === "object" && value !== null && !Array.isArray(value);
3528
- dialSdkDebugWarn(DEBUG_MODULE, "isRecord", "returned", {
3529
- value,
3530
- result
3531
- });
3532
- return result;
3533
- };
3534
3578
  //#endregion
3535
3579
  export { BATTERY_HEALTH_LABELS, CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, CAR_RUNNING_STATUS_PRIORITY, DEFAULT_DRIVING_STATUS_TUNING, DrivingExpressionResolutionError, DrivingExpressionsConfigError, OFFICIAL_DRIVING_EXPRESSIONS_CONFIG, OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, buildVehicleFrame, classifyDrivingSnapshot, createDrivingExpressionPlayer, createDrivingStatusController, extractDrivingSnapshotMetrics, isPublicDrivingMediaUrl, parseDrivingExpressionsConfig, readDrivingExpressionsConfig, resolveDrivingExpression, resolveDrivingExpressionStrict, resolveDrivingStatusTuning, tryParseDrivingExpressionsConfig, unifiedSensorInfo, waitForDrivingExpressionsConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziztechnology/dial-library",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "private": false,
5
5
  "description": "Tooony 表盘工具函数、方法",
6
6
  "files": [