@ziztechnology/dial-library 0.0.11 → 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/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,11 +125,15 @@ 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,
133
+ turnYawDominanceThreshold: .6,
134
+ turnLateralEvidenceThreshold: .25,
135
+ turnVerticalNoiseFloor: .1,
136
+ turnVerticalBumpRatio: 1.25,
94
137
  turnExitYawThreshold: .04,
95
138
  turnExitConfirmationMs: 700,
96
139
  turnClosureWindowMs: 1500
@@ -154,10 +197,16 @@ const resolveDrivingStatusTuning = (overrides = {}) => {
154
197
  "turnYawThreshold",
155
198
  "turnConfirmationMs",
156
199
  "turnIntegratedAngleThreshold",
200
+ "turnLateralEvidenceThreshold",
201
+ "turnVerticalNoiseFloor",
202
+ "turnVerticalBumpRatio",
157
203
  "turnExitYawThreshold",
158
204
  "turnExitConfirmationMs",
159
205
  "turnClosureWindowMs"
160
- ]) assertPositiveFinite(field, tuning[field]);
206
+ ]) requirePositiveFinite(field, tuning[field], {
207
+ moduleName: DEBUG_MODULE$5,
208
+ scope: "assertPositiveFinite"
209
+ });
161
210
  for (const field of [
162
211
  "motionMinimumSamples",
163
212
  "longitudinalMinimumSamples",
@@ -167,7 +216,8 @@ const resolveDrivingStatusTuning = (overrides = {}) => {
167
216
  for (const field of [
168
217
  "longitudinalSignCoherence",
169
218
  "rapidLongitudinalSignCoherence",
170
- "turnSignCoherence"
219
+ "turnSignCoherence",
220
+ "turnYawDominanceThreshold"
171
221
  ]) {
172
222
  const value = tuning[field];
173
223
  if (!Number.isFinite(value) || value <= .5 || value > 1) throwLoggedRangeError("resolveDrivingStatusTuning", `${field} must be greater than 0.5 and less than or equal to 1`);
@@ -192,22 +242,25 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
192
242
  dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid capture time", { info });
193
243
  return null;
194
244
  }
195
- const linearAcceleration = availableFreshVector(info.linearAcceleration, info.capturedAtMs, tuning.maxSampleAgeMs);
196
- const gyroscope = availableFreshVector(info.gyroscope, info.capturedAtMs, tuning.maxSampleAgeMs);
197
- const gravityMetric = availableFreshVector(info.gravity, info.capturedAtMs, tuning.maxSampleAgeMs);
198
- if (linearAcceleration === null || gyroscope === null || gravityMetric === null) {
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);
248
+ const gravity = availableCorrectedGravity(correction, info.capturedAtMs, tuning.maxSampleAgeMs) ?? (correction.filteredGravity !== void 0 && gravityMetric !== null ? {
249
+ value: correction.filteredGravity,
250
+ sampledAtMs: gravityMetric.sampledAtMs
251
+ } : gravityMetric);
252
+ if (linearAcceleration === null || gyroscope === null || gravity === null) {
199
253
  dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned unavailable vectors", {
200
254
  linearAcceleration,
201
255
  gyroscope,
202
- gravity: gravityMetric
256
+ gravity
203
257
  });
204
258
  return null;
205
259
  }
206
- const gravity = correction.filteredGravity ?? gravityMetric.value;
207
- const frame = buildVehicleFrame(gravity, tuning.vehicleRightMinimumNorm);
260
+ const frame = buildVehicleFrame(gravity.value, tuning.vehicleRightMinimumNorm);
208
261
  if (frame === null) {
209
262
  dialSdkDebugWarn(DEBUG_MODULE$5, "extractDrivingSnapshotMetrics", "returned invalid vehicle frame", {
210
- gravity,
263
+ gravity: gravity.value,
211
264
  vehicleRightMinimumNorm: tuning.vehicleRightMinimumNorm
212
265
  });
213
266
  return null;
@@ -218,7 +271,7 @@ const extractDrivingSnapshotMetrics = (info, tuning = DEFAULT_DRIVING_STATUS_TUN
218
271
  capturedAtMs: info.capturedAtMs,
219
272
  linearAccelerationSampledAtMs: linearAcceleration.sampledAtMs,
220
273
  gyroscopeSampledAtMs: gyroscope.sampledAtMs,
221
- gravitySampledAtMs: gravityMetric.sampledAtMs,
274
+ gravitySampledAtMs: gravity.sampledAtMs,
222
275
  longitudinal: dot(correctedLinear, frame.forward),
223
276
  lateral: dot(correctedLinear, frame.right),
224
277
  vertical: dot(correctedLinear, frame.up),
@@ -287,16 +340,17 @@ const classifyInstantaneousEvidence = (metrics, tuning) => {
287
340
  rejectionReason: null
288
341
  };
289
342
  };
290
- const availableFreshVector = (metric, capturedAtMs, maxSampleAgeMs) => {
291
- if (!metric?.available || !isFresh(metric.sampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
292
- const { x, y, z } = metric.value;
343
+ const availableCorrectedGravity = (correction, capturedAtMs, maxSampleAgeMs) => {
344
+ const { filteredGravity, filteredGravitySampledAtMs } = correction;
345
+ if (filteredGravity === void 0 || filteredGravitySampledAtMs === void 0 || !isFresh(filteredGravitySampledAtMs, capturedAtMs, maxSampleAgeMs)) return null;
346
+ const { x, y, z } = filteredGravity;
293
347
  return [
294
348
  x,
295
349
  y,
296
350
  z
297
351
  ].every(Number.isFinite) ? {
298
- value: metric.value,
299
- sampledAtMs: metric.sampledAtMs
352
+ value: filteredGravity,
353
+ sampledAtMs: filteredGravitySampledAtMs
300
354
  } : null;
301
355
  };
302
356
  const isFresh = (sampledAtMs, capturedAtMs, maxSampleAgeMs) => {
@@ -325,13 +379,6 @@ const normalize = (value) => {
325
379
  const length = magnitude(value);
326
380
  return Number.isFinite(length) && length > Number.EPSILON ? scale(value, 1 / length) : null;
327
381
  };
328
- const assertPositiveFinite = (name, value) => {
329
- if (!Number.isFinite(value) || value <= 0) {
330
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
331
- dialSdkDebugWarn(DEBUG_MODULE$5, "assertPositiveFinite", "threw", error);
332
- throw error;
333
- }
334
- };
335
382
  const throwLoggedRangeError = (scope, message) => {
336
383
  const error = new RangeError(message);
337
384
  dialSdkDebugWarn(DEBUG_MODULE$5, scope, "threw", error);
@@ -397,8 +444,51 @@ const CAR_RUNNING_STATUS_PRIORITY = {
397
444
  SUDDEN_BRAKING: 5
398
445
  };
399
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
400
478
  //#region src/sensor/driving_expressions.ts
401
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
+ };
402
492
  const OFFICIAL_DRIVING_EXPRESSION_FALLBACKS = Object.freeze({
403
493
  STOPPED: Object.freeze({
404
494
  kind: "emoji",
@@ -466,7 +556,7 @@ const parseDrivingExpressionsConfig = (raw, options = {}) => {
466
556
  options
467
557
  });
468
558
  const parsed = parseJsonObject(raw);
469
- 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");
470
560
  const knownStatuses = new Set(CAR_RUNNING_STATUSES);
471
561
  const stateKeys = Object.keys(parsed.states);
472
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");
@@ -492,6 +582,10 @@ const tryParseDrivingExpressionsConfig = (raw, options) => {
492
582
  dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "returned", { result });
493
583
  return result;
494
584
  } catch (error) {
585
+ if (!(error instanceof DrivingExpressionsConfigError)) {
586
+ dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "threw unexpected error", error);
587
+ throw error;
588
+ }
495
589
  dialSdkDebugWarn(DEBUG_MODULE$3, "tryParseDrivingExpressionsConfig", "returned null after parse error", error);
496
590
  return null;
497
591
  }
@@ -520,7 +614,7 @@ const readDrivingExpressionsConfig = (raw, options) => {
520
614
  const waitForDrivingExpressionsConfig = (options = {}) => {
521
615
  dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig", "called", { options });
522
616
  const timeoutMs = requireNonNegativeFinite("timeoutMs", options.timeoutMs ?? 3e3);
523
- const pollIntervalMs = requirePositiveFinite$1("pollIntervalMs", options.pollIntervalMs ?? 100);
617
+ const pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 100, REQUIRE_POSITIVE_FINITE_DEBUG$1);
524
618
  const { signal } = options;
525
619
  return new Promise((resolve, reject) => {
526
620
  const deadlineMs = Date.now() + timeoutMs;
@@ -568,7 +662,7 @@ const waitForDrivingExpressionsConfig = (options = {}) => {
568
662
  };
569
663
  const handleAbort = () => {
570
664
  dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig.handleAbort", "called");
571
- fail(createAbortError$1());
665
+ fail(createAbortError("The operation was aborted", CREATE_ABORT_ERROR_DEBUG$1));
572
666
  };
573
667
  const read = () => {
574
668
  dialSdkDebugWarn(DEBUG_MODULE$3, "waitForDrivingExpressionsConfig.read", "called");
@@ -586,7 +680,13 @@ const waitForDrivingExpressionsConfig = (options = {}) => {
586
680
  handleAbort();
587
681
  return;
588
682
  }
589
- const config = read();
683
+ let config;
684
+ try {
685
+ config = read();
686
+ } catch (error) {
687
+ fail(error);
688
+ return;
689
+ }
590
690
  if (config !== null) {
591
691
  finish(config);
592
692
  return;
@@ -881,7 +981,7 @@ const parseDrivingExpressionMedia = (raw, status, options) => {
881
981
  dialSdkDebugWarn(DEBUG_MODULE$3, "parseDrivingExpressionMedia", "returned legacy image", { result });
882
982
  return result;
883
983
  }
884
- 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");
885
985
  const mediaAssetId = parseMediaAssetId(raw.mediaAssetId, status);
886
986
  assertFieldsAbsent(raw, status, [
887
987
  "items",
@@ -987,7 +1087,7 @@ const parseJsonObject = (raw) => {
987
1087
  });
988
1088
  throw error;
989
1089
  }
990
- if (!isRecord$1(value)) {
1090
+ if (!isRecord(value, IS_RECORD_DEBUG$1)) {
991
1091
  const error = new DrivingExpressionsConfigError("DRIVING_EXPRESSIONS_INVALID_SCHEMA", "drivingExpressions must be a JSON object or JSON object string");
992
1092
  dialSdkDebugWarn(DEBUG_MODULE$3, "parseJsonObject", "threw", error);
993
1093
  throw error;
@@ -1004,7 +1104,7 @@ const readRuntimeDrivingExpressionsValue = () => {
1004
1104
  return result;
1005
1105
  }
1006
1106
  const faceConfig = runtimeGlobal.__TOOOONY_FACE_CONFIG__;
1007
- if (!isRecord$1(faceConfig)) {
1107
+ if (!isRecord(faceConfig, IS_RECORD_DEBUG$1)) {
1008
1108
  dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned", { result: void 0 });
1009
1109
  return;
1010
1110
  }
@@ -1013,7 +1113,7 @@ const readRuntimeDrivingExpressionsValue = () => {
1013
1113
  dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned face config", { result });
1014
1114
  return result;
1015
1115
  }
1016
- 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;
1017
1117
  dialSdkDebugWarn(DEBUG_MODULE$3, "readRuntimeDrivingExpressionsValue", "returned legacy face config", { result });
1018
1118
  return result;
1019
1119
  };
@@ -1135,14 +1235,6 @@ const compact = (value) => {
1135
1235
  dialSdkDebugWarn(DEBUG_MODULE$3, "compact", "returned", { result: value });
1136
1236
  return value;
1137
1237
  };
1138
- const isRecord$1 = (value) => {
1139
- const result = typeof value === "object" && value !== null && !Array.isArray(value);
1140
- dialSdkDebugWarn(DEBUG_MODULE$3, "isRecord", "returned", {
1141
- value,
1142
- result
1143
- });
1144
- return result;
1145
- };
1146
1238
  const requireNonNegativeFinite = (name, value) => {
1147
1239
  dialSdkDebugWarn(DEBUG_MODULE$3, "requireNonNegativeFinite", "called", {
1148
1240
  name,
@@ -1156,31 +1248,6 @@ const requireNonNegativeFinite = (name, value) => {
1156
1248
  dialSdkDebugWarn(DEBUG_MODULE$3, "requireNonNegativeFinite", "returned", { result: value });
1157
1249
  return value;
1158
1250
  };
1159
- const requirePositiveFinite$1 = (name, value) => {
1160
- dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "called", {
1161
- name,
1162
- value
1163
- });
1164
- if (!Number.isFinite(value) || value <= 0) {
1165
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
1166
- dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "threw", error);
1167
- throw error;
1168
- }
1169
- dialSdkDebugWarn(DEBUG_MODULE$3, "requirePositiveFinite", "returned", { result: value });
1170
- return value;
1171
- };
1172
- const createAbortError$1 = () => {
1173
- dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "called");
1174
- if (typeof DOMException === "function") {
1175
- const result = new DOMException("The operation was aborted", "AbortError");
1176
- dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "returned DOMException", { result });
1177
- return result;
1178
- }
1179
- const error = /* @__PURE__ */ new Error("The operation was aborted");
1180
- error.name = "AbortError";
1181
- dialSdkDebugWarn(DEBUG_MODULE$3, "createAbortError", "returned Error", { result: error });
1182
- return error;
1183
- };
1184
1251
  //#endregion
1185
1252
  //#region src/sensor/page_lifecycle.ts
1186
1253
  const DEBUG_MODULE$2 = "page_lifecycle.ts";
@@ -1315,12 +1382,9 @@ const dispatchResume = () => {
1315
1382
  };
1316
1383
  const safeInvoke = (callback) => {
1317
1384
  dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "called");
1318
- try {
1319
- callback();
1320
- dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "returned");
1321
- } catch (error) {
1385
+ if (invokeSafely(callback, [], (error) => {
1322
1386
  dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "callback threw", error);
1323
- }
1387
+ })) dialSdkDebugWarn(DEBUG_MODULE$2, "safeInvoke", "returned");
1324
1388
  };
1325
1389
  //#endregion
1326
1390
  //#region src/sensor/driving_status_controller.ts
@@ -1330,6 +1394,10 @@ const ZERO_VECTOR = {
1330
1394
  y: 0,
1331
1395
  z: 0
1332
1396
  };
1397
+ const REQUIRE_POSITIVE_FINITE_DEBUG = {
1398
+ moduleName: DEBUG_MODULE$1,
1399
+ scope: "requirePositiveFinite"
1400
+ };
1333
1401
  const createDrivingStatusController = (options = {}) => {
1334
1402
  dialSdkDebugWarn(DEBUG_MODULE$1, "createDrivingStatusController", "called", { options });
1335
1403
  const result = new DefaultDrivingStatusController(options);
@@ -1363,6 +1431,18 @@ var DefaultDrivingStatusController = class {
1363
1431
  this.turnStatus = null;
1364
1432
  this.turnQuietSinceMs = null;
1365
1433
  this.oppositeTurnSinceMs = null;
1434
+ this.detectorCandidates = {
1435
+ base: null,
1436
+ rapid_longitudinal: null,
1437
+ longitudinal: null,
1438
+ turn: null
1439
+ };
1440
+ this.detectorRejections = {
1441
+ base: null,
1442
+ rapid_longitudinal: null,
1443
+ longitudinal: null,
1444
+ turn: null
1445
+ };
1366
1446
  this.requestedRunning = false;
1367
1447
  this.polling = false;
1368
1448
  this.lifecycleSuspended = false;
@@ -1401,9 +1481,9 @@ var DefaultDrivingStatusController = class {
1401
1481
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.handleLifecycleResume", "returned");
1402
1482
  };
1403
1483
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.constructor", "called", { options });
1404
- this.sensorProvider = options.sensorProvider ?? defaultSensorProvider;
1405
- this.pollIntervalMs = requirePositiveFinite("pollIntervalMs", options.pollIntervalMs ?? 200);
1406
- 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);
1407
1487
  this.tuning = resolveDrivingStatusTuning(options.tuning);
1408
1488
  this.clock = options.clock ?? systemClock;
1409
1489
  this.onDiagnostic = options.onDiagnostic;
@@ -1567,25 +1647,26 @@ var DefaultDrivingStatusController = class {
1567
1647
  });
1568
1648
  return;
1569
1649
  }
1570
- const gravity = this.updateFilteredGravity(payload);
1650
+ const gravity = this.resolveGravityFrame(payload);
1571
1651
  const metrics = gravity === null ? null : extractDrivingSnapshotMetrics(payload, this.tuning, {
1572
- filteredGravity: gravity,
1652
+ filteredGravity: gravity.value,
1653
+ filteredGravitySampledAtMs: gravity.sampledAtMs,
1573
1654
  linearAccelerationBias: this.linearAccelerationBias,
1574
1655
  gyroscopeBias: this.gyroscopeBias
1575
1656
  });
1576
- const accelerometerSample = extractFreshVectorMetric(payload.accelerometer, payload.capturedAtMs, this.tuning.maxSampleAgeMs);
1577
- if (metrics === null || accelerometerSample === null) {
1657
+ const accelerometerSample = extractFreshVector(payload.accelerometer, payload.capturedAtMs, this.tuning.maxSampleAgeMs);
1658
+ if (gravity === null || metrics === null || accelerometerSample === null) {
1578
1659
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "invalid or stale snapshot", {
1579
1660
  metrics,
1580
1661
  accelerometerSample
1581
1662
  });
1582
1663
  this.processUnavailable("invalid_or_stale_snapshot");
1583
1664
  } else {
1584
- this.processAvailable(metrics, accelerometerSample);
1665
+ this.processAvailable(metrics, accelerometerSample, gravity);
1585
1666
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "processed sample", { capturedAtMs: metrics.capturedAtMs });
1586
1667
  }
1587
1668
  } catch (error) {
1588
- if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError$1(error)) {
1669
+ if (!this.polling || generation !== this.generation || pendingRead.signal.aborted || isAbortError(error)) {
1589
1670
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.poll", "ignored provider error", { error });
1590
1671
  return;
1591
1672
  }
@@ -1600,21 +1681,44 @@ var DefaultDrivingStatusController = class {
1600
1681
  });
1601
1682
  }
1602
1683
  }
1603
- updateFilteredGravity(info) {
1604
- const sample = extractFreshVectorMetric(info.gravity, info.capturedAtMs, this.tuning.maxSampleAgeMs);
1605
- if (sample === null) return null;
1606
- if (sample.sampledAtMs === this.lastGravitySampledAtMs) return this.filteredGravity;
1684
+ resolveGravityFrame(info) {
1685
+ const sample = extractFreshVector(info.gravity, info.capturedAtMs, this.tuning.maxSampleAgeMs);
1686
+ if (sample === null) {
1687
+ if (this.filteredGravity === null || this.lastGravitySampledAtMs === null) return null;
1688
+ const ageMs = info.capturedAtMs - this.lastGravitySampledAtMs;
1689
+ return ageMs >= 0 && ageMs <= this.tuning.maxSampleAgeMs ? {
1690
+ value: this.filteredGravity,
1691
+ sampledAtMs: this.lastGravitySampledAtMs,
1692
+ source: "cached"
1693
+ } : null;
1694
+ }
1695
+ if (sample.sampledAtMs === this.lastGravitySampledAtMs && this.filteredGravity !== null) return {
1696
+ value: this.filteredGravity,
1697
+ sampledAtMs: sample.sampledAtMs,
1698
+ source: "live"
1699
+ };
1607
1700
  if (this.filteredGravity === null || this.lastGravitySampledAtMs === null) this.filteredGravity = { ...sample.value };
1608
1701
  else {
1609
- if (sample.sampledAtMs < this.lastGravitySampledAtMs) return this.filteredGravity;
1702
+ if (sample.sampledAtMs < this.lastGravitySampledAtMs) {
1703
+ const ageMs = info.capturedAtMs - this.lastGravitySampledAtMs;
1704
+ return ageMs >= 0 && ageMs <= this.tuning.maxSampleAgeMs ? {
1705
+ value: this.filteredGravity,
1706
+ sampledAtMs: this.lastGravitySampledAtMs,
1707
+ source: "cached"
1708
+ } : null;
1709
+ }
1610
1710
  const elapsedMs = Math.max(0, sample.sampledAtMs - this.lastGravitySampledAtMs);
1611
1711
  const alpha = 1 - Math.exp(-elapsedMs / this.tuning.gravityFilterTimeConstantMs);
1612
1712
  this.filteredGravity = lerpVector(this.filteredGravity, sample.value, alpha);
1613
1713
  }
1614
1714
  this.lastGravitySampledAtMs = sample.sampledAtMs;
1615
- return this.filteredGravity;
1715
+ return {
1716
+ value: this.filteredGravity,
1717
+ sampledAtMs: sample.sampledAtMs,
1718
+ source: "live"
1719
+ };
1616
1720
  }
1617
- processAvailable(metrics, accelerometerSample) {
1721
+ processAvailable(metrics, accelerometerSample, gravity) {
1618
1722
  dialSdkDebugWarn(DEBUG_MODULE$1, "DefaultDrivingStatusController.processAvailable", "called", {
1619
1723
  metrics,
1620
1724
  accelerometerSample
@@ -1627,6 +1731,8 @@ var DefaultDrivingStatusController = class {
1627
1731
  type: "sample_available",
1628
1732
  atMs: now,
1629
1733
  capturedAtMs: metrics.capturedAtMs,
1734
+ frameSource: gravity.source,
1735
+ frameAgeMs: metrics.capturedAtMs - gravity.sampledAtMs,
1630
1736
  tuningVersion: this.tuning.version
1631
1737
  });
1632
1738
  const addedLongitudinal = pushUniqueMetric(this.longitudinalWindow, metrics, "linearAccelerationSampledAtMs", Math.max(this.tuning.longitudinalWindowMs, this.tuning.rapidLongitudinalWindowMs));
@@ -1637,6 +1743,10 @@ var DefaultDrivingStatusController = class {
1637
1743
  return;
1638
1744
  }
1639
1745
  const motionMad = calculateCombinedAxisMad(this.motionWindow, this.tuning.motionMinimumSamples);
1746
+ const previousBaseStatus = this.baseStatus;
1747
+ const previousUrgentStatus = this.urgentStatus;
1748
+ const previousLongitudinalStatus = this.normalLongitudinalStatus;
1749
+ const previousTurnStatus = this.turnStatus;
1640
1750
  const baseCondition = this.updateBaseDetector(motionMad, metrics, accelerometerSample.sampledAtMs, addedMotion);
1641
1751
  const longitudinalEvidence = detectLongitudinalEvidence(this.longitudinalWindow, this.tuning);
1642
1752
  if (addedLongitudinal) this.updateLongitudinalDetector(longitudinalEvidence, metrics.linearAccelerationSampledAtMs);
@@ -1648,6 +1758,27 @@ var DefaultDrivingStatusController = class {
1648
1758
  this.stationaryConfirmed = false;
1649
1759
  }
1650
1760
  if (this.stationaryConfirmed) this.updateBias(metrics);
1761
+ const statistics = {
1762
+ base: {
1763
+ sampleCount: this.motionWindow.length,
1764
+ durationMs: vectorWindowDuration(this.motionWindow),
1765
+ motionMad,
1766
+ gyroMagnitude: metrics.gyroMagnitude,
1767
+ rawGyroMagnitude: metrics.rawGyroMagnitude
1768
+ },
1769
+ longitudinal: longitudinalEvidence.normalStatistics,
1770
+ rapidLongitudinal: longitudinalEvidence.rapidStatistics,
1771
+ turn: turnEvidence.statistics
1772
+ };
1773
+ const baseCandidate = baseCondition === "stationary" ? "STOPPED" : baseCondition === "moving" ? "STEADY_DRIVING" : null;
1774
+ this.updateDetectorCandidate("base", baseCandidate, null, statistics, now);
1775
+ this.updateDetectorCandidate("rapid_longitudinal", longitudinalEvidence.rapidCandidate, longitudinalEvidence.rejectionReason, statistics, now);
1776
+ this.updateDetectorCandidate("longitudinal", longitudinalEvidence.normalCandidate, null, statistics, now);
1777
+ this.updateDetectorCandidate("turn", turnEvidence.candidate, turnEvidence.rejectionReason, statistics, now);
1778
+ this.emitDetectorConfirmation("base", previousBaseStatus, this.baseStatus, statistics, now);
1779
+ this.emitDetectorConfirmation("rapid_longitudinal", previousUrgentStatus, this.urgentStatus, statistics, now);
1780
+ this.emitDetectorConfirmation("longitudinal", previousLongitudinalStatus, this.normalLongitudinalStatus, statistics, now);
1781
+ this.emitDetectorConfirmation("turn", previousTurnStatus, this.turnStatus, statistics, now);
1651
1782
  const pendingCandidate = instantaneousCandidate(metrics, this.tuning);
1652
1783
  this.setCandidate(pendingCandidate === this.status ? null : pendingCandidate, observationTime(metrics));
1653
1784
  this.emitDiagnostic({
@@ -1658,7 +1789,10 @@ var DefaultDrivingStatusController = class {
1658
1789
  rapidCandidate: longitudinalEvidence.rapidCandidate,
1659
1790
  longitudinalCandidate: longitudinalEvidence.normalCandidate,
1660
1791
  turnCandidate: turnEvidence.candidate,
1661
- rejectionReason: longitudinalEvidence.rejectionReason,
1792
+ rejectionReason: longitudinalEvidence.rejectionReason ?? turnEvidence.rejectionReason,
1793
+ frameSource: gravity.source,
1794
+ frameAgeMs: metrics.capturedAtMs - gravity.sampledAtMs,
1795
+ statistics,
1662
1796
  tuningVersion: this.tuning.version
1663
1797
  });
1664
1798
  const hasAction = this.urgentStatus !== null || this.turnStatus !== null || this.normalLongitudinalStatus !== null;
@@ -1766,8 +1900,41 @@ var DefaultDrivingStatusController = class {
1766
1900
  }
1767
1901
  this.lastBiasSampledAtMs = sampledAtMs;
1768
1902
  }
1903
+ updateDetectorCandidate(detector, candidate, rejectionReason, statistics, atMs) {
1904
+ const previousCandidate = this.detectorCandidates[detector];
1905
+ if (previousCandidate !== candidate) {
1906
+ if (previousCandidate !== null) this.emitDetectorTransition(detector, "candidate_exited", previousCandidate, null, statistics, atMs);
1907
+ if (candidate !== null) this.emitDetectorTransition(detector, "candidate_entered", candidate, null, statistics, atMs);
1908
+ this.detectorCandidates[detector] = candidate;
1909
+ }
1910
+ if (rejectionReason !== null && rejectionReason !== this.detectorRejections[detector]) this.emitDetectorTransition(detector, "candidate_rejected", null, rejectionReason, statistics, atMs);
1911
+ this.detectorRejections[detector] = rejectionReason;
1912
+ }
1913
+ emitDetectorConfirmation(detector, previousStatus, status, statistics, atMs) {
1914
+ if (status !== null && status !== previousStatus) this.emitDetectorTransition(detector, "candidate_confirmed", status, null, statistics, atMs);
1915
+ }
1916
+ emitDetectorTransition(detector, phase, status, rejectionReason, statistics, atMs) {
1917
+ this.emitDiagnostic({
1918
+ type: "detector_transition",
1919
+ atMs,
1920
+ detector,
1921
+ phase,
1922
+ status,
1923
+ rejectionReason,
1924
+ statistics,
1925
+ tuningVersion: this.tuning.version
1926
+ });
1927
+ }
1769
1928
  commitSelectedStatus(now) {
1770
- 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);
1771
1938
  this.commit(selected, now, "candidate_confirmed");
1772
1939
  }
1773
1940
  processUnavailable(category) {
@@ -1825,7 +1992,12 @@ var DefaultDrivingStatusController = class {
1825
1992
  reason,
1826
1993
  atMs: now
1827
1994
  };
1828
- 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
+ });
1829
2001
  this.emitDiagnostic({
1830
2002
  type: "status_committed",
1831
2003
  atMs: now,
@@ -1868,6 +2040,10 @@ var DefaultDrivingStatusController = class {
1868
2040
  this.turnStatus = null;
1869
2041
  this.turnQuietSinceMs = null;
1870
2042
  this.oppositeTurnSinceMs = null;
2043
+ for (const detector of Object.keys(this.detectorCandidates)) {
2044
+ this.detectorCandidates[detector] = null;
2045
+ this.detectorRejections[detector] = null;
2046
+ }
1871
2047
  }
1872
2048
  resetTransientRecognition() {
1873
2049
  this.clearRecognitionWindows();
@@ -1883,7 +2059,12 @@ var DefaultDrivingStatusController = class {
1883
2059
  event,
1884
2060
  hasListener: this.onDiagnostic !== void 0
1885
2061
  });
1886
- 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
+ });
1887
2068
  }
1888
2069
  attachLifecycleListeners() {
1889
2070
  if (!this.managePageLifecycle || this.unsubscribePageLifecycle !== null) return;
@@ -1899,36 +2080,57 @@ const detectLongitudinalEvidence = (window, tuning) => {
1899
2080
  const normalWindow = recentMetrics(window, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs");
1900
2081
  let rapidCandidate = null;
1901
2082
  let rejectionReason = null;
2083
+ const rapidValues = rapidWindow.map((sample) => sample.longitudinal);
2084
+ const rapidMean = trimmedMean(rapidValues);
2085
+ const rapidDirection = Math.sign(rapidMean);
2086
+ const rapidCoherence = signCoherence(rapidValues, rapidDirection, tuning.longitudinalExitThreshold);
2087
+ const rapidPeak = maximumAbsolute(rapidValues);
2088
+ const longitudinalRms = rootMeanSquare(rapidValues);
2089
+ const verticalRms = rootMeanSquare(rapidWindow.map((sample) => sample.vertical));
2090
+ const reversalWindow = recentMetrics(window, tuning.longitudinalReversalWindowMs, "linearAccelerationSampledAtMs");
2091
+ const hasReversal = containsBothSigns(reversalWindow.map((sample) => sample.longitudinal), tuning.longitudinalThreshold);
2092
+ const rapidStatistics = rapidWindow.length === 0 ? null : {
2093
+ sampleCount: rapidWindow.length,
2094
+ durationMs: metricWindowDuration(rapidWindow, "linearAccelerationSampledAtMs"),
2095
+ mean: rapidMean,
2096
+ peak: rapidPeak,
2097
+ coherence: rapidCoherence,
2098
+ longitudinalRms,
2099
+ verticalRms,
2100
+ hasReversal
2101
+ };
1902
2102
  if (windowCovers(rapidWindow, tuning.rapidLongitudinalMinimumSamples, tuning.rapidLongitudinalWindowMs, "linearAccelerationSampledAtMs")) {
1903
- const values = rapidWindow.map((sample) => sample.longitudinal);
1904
- const direction = Math.sign(trimmedMean(values));
1905
- const coherence = signCoherence(values, direction, tuning.longitudinalExitThreshold);
1906
- const peak = Math.max(...values.map(Math.abs));
1907
- const mean = Math.abs(trimmedMean(values));
1908
- const longitudinalRms = rootMeanSquare(values);
1909
- const verticalRms = rootMeanSquare(rapidWindow.map((sample) => sample.vertical));
1910
- const reversalWindow = recentMetrics(window, tuning.longitudinalReversalWindowMs, "linearAccelerationSampledAtMs");
1911
- const hasReversal = containsBothSigns(reversalWindow.map((sample) => sample.longitudinal), tuning.longitudinalThreshold);
1912
- if (peak >= tuning.rapidLongitudinalPeakThreshold && mean >= tuning.rapidLongitudinalMeanThreshold) {
2103
+ if (rapidPeak >= tuning.rapidLongitudinalPeakThreshold && Math.abs(rapidMean) >= tuning.rapidLongitudinalMeanThreshold) {
1913
2104
  if (verticalRms > longitudinalRms * tuning.verticalBumpRatio) rejectionReason = "vertical_bump";
1914
2105
  else if (hasReversal) rejectionReason = "longitudinal_reversal";
1915
- else if (direction !== 0 && coherence >= tuning.rapidLongitudinalSignCoherence) rapidCandidate = direction > 0 ? "RAPID_ACCELERATION" : "SUDDEN_BRAKING";
2106
+ else if (rapidDirection !== 0 && rapidCoherence >= tuning.rapidLongitudinalSignCoherence) rapidCandidate = rapidDirection > 0 ? "RAPID_ACCELERATION" : "SUDDEN_BRAKING";
1916
2107
  }
1917
2108
  }
1918
2109
  let normalCandidate = null;
1919
2110
  let retainedNormal = false;
2111
+ let normalStatistics = null;
1920
2112
  if (normalWindow.length > 0) {
1921
2113
  const values = normalWindow.map((sample) => sample.longitudinal);
1922
2114
  const mean = trimmedMean(values);
1923
2115
  const direction = Math.sign(mean);
2116
+ const coherence = signCoherence(values, direction, tuning.longitudinalExitThreshold);
2117
+ normalStatistics = {
2118
+ sampleCount: normalWindow.length,
2119
+ durationMs: metricWindowDuration(normalWindow, "linearAccelerationSampledAtMs"),
2120
+ mean,
2121
+ peak: maximumAbsolute(values),
2122
+ coherence
2123
+ };
1924
2124
  retainedNormal = Math.abs(mean) >= tuning.longitudinalExitThreshold;
1925
- if (windowCovers(normalWindow, tuning.longitudinalMinimumSamples, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs") && Math.abs(mean) >= tuning.longitudinalThreshold && signCoherence(values, direction, tuning.longitudinalExitThreshold) >= tuning.longitudinalSignCoherence) normalCandidate = direction > 0 ? "ACCELERATION" : "BRAKING";
2125
+ if (windowCovers(normalWindow, tuning.longitudinalMinimumSamples, tuning.longitudinalWindowMs, "linearAccelerationSampledAtMs") && Math.abs(mean) >= tuning.longitudinalThreshold && coherence >= tuning.longitudinalSignCoherence) normalCandidate = direction > 0 ? "ACCELERATION" : "BRAKING";
1926
2126
  }
1927
2127
  return {
1928
2128
  rapidCandidate,
1929
2129
  normalCandidate,
1930
2130
  retainedNormal,
1931
- rejectionReason
2131
+ rejectionReason,
2132
+ normalStatistics,
2133
+ rapidStatistics
1932
2134
  };
1933
2135
  };
1934
2136
  const detectTurnEvidence = (window, tuning) => {
@@ -1936,20 +2138,47 @@ const detectTurnEvidence = (window, tuning) => {
1936
2138
  if (!latest) return {
1937
2139
  candidate: null,
1938
2140
  latestYaw: 0,
1939
- sampledAtMs: 0
2141
+ sampledAtMs: 0,
2142
+ rejectionReason: null,
2143
+ statistics: null
1940
2144
  };
1941
2145
  const strongWindow = recentMetrics(window, tuning.turnConfirmationMs, "gyroscopeSampledAtMs");
1942
2146
  const strongMean = trimmedMean(strongWindow.map((sample) => sample.yaw));
1943
2147
  const strongDirection = Math.sign(strongMean);
1944
- const strong = windowCovers(strongWindow, tuning.turnMinimumSamples, tuning.turnConfirmationMs, "gyroscopeSampledAtMs") && Math.abs(strongMean) >= tuning.turnYawThreshold && signCoherence(strongWindow.map((sample) => sample.yaw), strongDirection, tuning.turnExitYawThreshold) >= tuning.turnSignCoherence;
2148
+ const strongCoherence = signCoherence(strongWindow.map((sample) => sample.yaw), strongDirection, tuning.turnExitYawThreshold);
2149
+ const strong = windowCovers(strongWindow, tuning.turnMinimumSamples, tuning.turnConfirmationMs, "gyroscopeSampledAtMs") && Math.abs(strongMean) >= tuning.turnYawThreshold && strongCoherence >= tuning.turnSignCoherence;
1945
2150
  const signedAngle = integrateYaw(window);
1946
2151
  const gentleDirection = Math.sign(signedAngle);
1947
- const gentle = window.length >= tuning.turnMinimumSamples && Math.abs(signedAngle) >= tuning.turnIntegratedAngleThreshold && signCoherence(window.map((sample) => sample.yaw), gentleDirection, tuning.turnExitYawThreshold) >= tuning.turnSignCoherence;
1948
- const direction = strong ? strongDirection : gentle ? gentleDirection : 0;
2152
+ const gentleCoherence = signCoherence(window.map((sample) => sample.yaw), gentleDirection, tuning.turnExitYawThreshold);
2153
+ const yawRms = rootMeanSquare(window.map((sample) => sample.yaw));
2154
+ const rawGyroRms = rootMeanSquare(window.map((sample) => sample.rawGyroMagnitude));
2155
+ const yawDominance = rawGyroRms <= Number.EPSILON ? 0 : Math.min(1, yawRms / rawGyroRms);
2156
+ const lateralRms = rootMeanSquare(window.map((sample) => sample.lateral));
2157
+ const verticalRms = rootMeanSquare(window.map((sample) => sample.vertical));
2158
+ const gentleShape = window.length >= tuning.turnMinimumSamples && Math.abs(signedAngle) >= tuning.turnIntegratedAngleThreshold && gentleCoherence >= tuning.turnSignCoherence;
2159
+ const verticalDominant = verticalRms > Math.max(lateralRms, tuning.turnVerticalNoiseFloor) * tuning.turnVerticalBumpRatio;
2160
+ const yawDominant = yawDominance >= tuning.turnYawDominanceThreshold;
2161
+ const laterallySupported = lateralRms >= tuning.turnLateralEvidenceThreshold;
2162
+ const rejectionReason = !strong && gentleShape && verticalDominant && !yawDominant ? "vertical_bump" : null;
2163
+ const direction = strong ? strongDirection : gentleShape && rejectionReason === null && (yawDominant || laterallySupported) ? gentleDirection : 0;
1949
2164
  return {
1950
2165
  candidate: direction === 0 ? null : direction > 0 ? "LEFT_TURN" : "RIGHT_TURN",
1951
2166
  latestYaw: latest.yaw,
1952
- sampledAtMs: latest.gyroscopeSampledAtMs
2167
+ sampledAtMs: latest.gyroscopeSampledAtMs,
2168
+ rejectionReason,
2169
+ statistics: {
2170
+ sampleCount: window.length,
2171
+ durationMs: metricWindowDuration(window, "gyroscopeSampledAtMs"),
2172
+ strongMean,
2173
+ strongCoherence,
2174
+ signedAngle,
2175
+ gentleCoherence,
2176
+ yawRms,
2177
+ rawGyroRms,
2178
+ yawDominance,
2179
+ lateralRms,
2180
+ verticalRms
2181
+ }
1953
2182
  };
1954
2183
  };
1955
2184
  const instantaneousCandidate = (metrics, tuning) => {
@@ -1990,6 +2219,12 @@ const signCoherence = (values, direction, deadband) => {
1990
2219
  };
1991
2220
  const containsBothSigns = (values, threshold) => values.some((value) => value >= threshold) && values.some((value) => value <= -threshold);
1992
2221
  const rootMeanSquare = (values) => values.length === 0 ? 0 : Math.sqrt(values.reduce((sum, value) => sum + value ** 2, 0) / values.length);
2222
+ const maximumAbsolute = (values) => values.length === 0 ? 0 : Math.max(...values.map(Math.abs));
2223
+ const metricWindowDuration = (values, timestampKey) => {
2224
+ const first = values[0];
2225
+ const last = values[values.length - 1];
2226
+ return first && last ? Math.max(0, last[timestampKey] - first[timestampKey]) : 0;
2227
+ };
1993
2228
  const calculateCombinedAxisMad = (samples, minimumSamples) => {
1994
2229
  if (samples.length < minimumSamples) return null;
1995
2230
  const medianX = median(samples.map((sample) => sample.value.x));
@@ -2000,6 +2235,11 @@ const calculateCombinedAxisMad = (samples, minimumSamples) => {
2000
2235
  const madZ = median(samples.map((sample) => Math.abs(sample.value.z - medianZ)));
2001
2236
  return Math.hypot(madX, madY, madZ);
2002
2237
  };
2238
+ const vectorWindowDuration = (values) => {
2239
+ const first = values[0];
2240
+ const last = values[values.length - 1];
2241
+ return first && last ? Math.max(0, last.sampledAtMs - first.sampledAtMs) : 0;
2242
+ };
2003
2243
  const median = (values) => {
2004
2244
  if (values.length === 0) return 0;
2005
2245
  const sorted = [...values].sort((left, right) => left - right);
@@ -2009,7 +2249,6 @@ const median = (values) => {
2009
2249
  const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
2010
2250
  const latest = window[window.length - 1];
2011
2251
  if (latest && metrics[timestampKey] <= latest[timestampKey]) return false;
2012
- if (window.some((value) => value[timestampKey] === metrics[timestampKey])) return false;
2013
2252
  window.push(metrics);
2014
2253
  const cutoffMs = metrics[timestampKey] - windowMs;
2015
2254
  while (window[0] && window[0][timestampKey] < cutoffMs) window.shift();
@@ -2018,67 +2257,44 @@ const pushUniqueMetric = (window, metrics, timestampKey, windowMs) => {
2018
2257
  const pushUniqueVector = (window, sample, windowMs) => {
2019
2258
  const latest = window[window.length - 1];
2020
2259
  if (latest && sample.sampledAtMs <= latest.sampledAtMs) return false;
2021
- if (window.some((value) => value.sampledAtMs === sample.sampledAtMs)) return false;
2022
2260
  window.push(sample);
2023
2261
  const cutoffMs = sample.sampledAtMs - windowMs;
2024
2262
  while (window[0] && window[0].sampledAtMs < cutoffMs) window.shift();
2025
2263
  return true;
2026
2264
  };
2027
- const extractFreshVectorMetric = (metric, capturedAtMs, maxSampleAgeMs) => {
2028
- if (!metric?.available || !Number.isFinite(capturedAtMs) || !Number.isFinite(metric.sampledAtMs)) return null;
2029
- const ageMs = capturedAtMs - metric.sampledAtMs;
2030
- if (ageMs < 0 || ageMs > maxSampleAgeMs) return null;
2031
- const { x, y, z } = metric.value;
2032
- return [
2033
- x,
2034
- y,
2035
- z
2036
- ].every(Number.isFinite) ? {
2037
- sampledAtMs: metric.sampledAtMs,
2038
- value: metric.value
2039
- } : null;
2040
- };
2041
2265
  const observationTime = (metrics) => Math.max(metrics.linearAccelerationSampledAtMs, metrics.gyroscopeSampledAtMs);
2042
2266
  const lerpVector = (from, to, alpha) => ({
2043
2267
  x: from.x + (to.x - from.x) * alpha,
2044
2268
  y: from.y + (to.y - from.y) * alpha,
2045
2269
  z: from.z + (to.z - from.z) * alpha
2046
2270
  });
2047
- const defaultSensorProvider = () => {
2048
- const runtimeGlobal = globalThis;
2049
- if (typeof runtimeGlobal.unifiedSensorInfo !== "function") return Promise.reject(/* @__PURE__ */ new Error("window.unifiedSensorInfo is unavailable"));
2050
- return runtimeGlobal.unifiedSensorInfo();
2051
- };
2052
2271
  const systemClock = {
2053
2272
  now: () => Date.now(),
2054
2273
  setTimeout: (callback, delayMs) => globalThis.setTimeout(callback, delayMs),
2055
2274
  clearTimeout: (handle) => globalThis.clearTimeout(handle)
2056
2275
  };
2057
2276
  const isBaseStatus = (status) => status === "STOPPED" || status === "STEADY_DRIVING";
2058
- const isAbortError$1 = (error) => error instanceof Error && error.name === "AbortError";
2059
- const requirePositiveFinite = (name, value) => {
2060
- if (!Number.isFinite(value) || value <= 0) {
2061
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
2062
- dialSdkDebugWarn(DEBUG_MODULE$1, "requirePositiveFinite", "threw", error);
2063
- throw error;
2064
- }
2065
- return value;
2066
- };
2067
- const safeCall = (callback, value) => {
2068
- try {
2069
- callback(value);
2070
- } catch (error) {
2071
- dialSdkDebugWarn(DEBUG_MODULE$1, "safeCall", "callback threw", {
2072
- error,
2073
- value
2074
- });
2075
- }
2076
- };
2077
2277
  const noop = () => void 0;
2078
2278
  //#endregion
2079
2279
  //#region src/sensor/driving_expression_player.ts
2080
2280
  const DEBUG_MODULE = "driving_expression_player.ts";
2081
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
+ };
2082
2298
  const createDrivingExpressionPlayer = (container, options) => {
2083
2299
  dialSdkDebugWarn(DEBUG_MODULE, "createDrivingExpressionPlayer", "called", {
2084
2300
  container,
@@ -2095,6 +2311,7 @@ var DefaultDrivingExpressionPlayer = class {
2095
2311
  this.mediaKind = null;
2096
2312
  this.errorCategory = null;
2097
2313
  this.desiredMedia = null;
2314
+ this.desiredMediaRole = null;
2098
2315
  this.userPaused = false;
2099
2316
  this.lifecycleSuspended = false;
2100
2317
  this.paused = false;
@@ -2143,15 +2360,15 @@ var DefaultDrivingExpressionPlayer = class {
2143
2360
  this.document = container.ownerDocument;
2144
2361
  this.config = options.config;
2145
2362
  this.fallbacks = options.fallbacks;
2146
- this.loadTimeoutMs = positiveFinite("loadTimeoutMs", options.loadTimeoutMs ?? 5e3);
2147
- 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);
2148
2365
  this.fit = options.fit ?? "contain";
2149
2366
  this.tgsRenderer = defaultTgsRenderer;
2150
2367
  this.fetchImplementation = options.fetch ?? globalThis.fetch?.bind(globalThis);
2151
2368
  this.onDiagnostic = options.onDiagnostic;
2152
- this.maxTgsCompressedBytes = positiveFinite("maxTgsCompressedBytes", options.maxTgsCompressedBytes ?? 2 * 1024 * 1024);
2153
- this.maxTgsJsonBytes = positiveFinite("maxTgsJsonBytes", options.maxTgsJsonBytes ?? 8 * 1024 * 1024);
2154
- 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);
2155
2372
  if (options.managePageLifecycle ?? true) this.unsubscribePageLifecycle = subscribeDrivingPageLifecycle(this.handleLifecyclePause, this.handleLifecycleResume);
2156
2373
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.constructor", "created", {
2157
2374
  loadTimeoutMs: this.loadTimeoutMs,
@@ -2176,24 +2393,33 @@ var DefaultDrivingExpressionPlayer = class {
2176
2393
  const operation = this.beginOperation();
2177
2394
  const configured = this.config.states[status];
2178
2395
  if (configured === void 0) {
2179
- const fallback = this.fallbacks?.[status];
2180
- if (fallback === void 0) {
2396
+ const fallback = this.resolveFallback(status);
2397
+ if (fallback.type === "missing") {
2398
+ this.desiredMedia = null;
2399
+ this.desiredMediaRole = null;
2181
2400
  this.handleFailure(status, null, new DrivingExpressionResolutionError(status), "invalid_config", operation, false);
2182
2401
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned after missing config");
2183
2402
  return;
2184
2403
  }
2185
- this.desiredMedia = fallback;
2404
+ this.desiredMedia = fallback.media;
2405
+ this.desiredMediaRole = "fallback";
2186
2406
  if (this.paused) {
2187
2407
  this.phase = "idle";
2188
2408
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "deferred fallback while paused");
2189
2409
  return;
2190
2410
  }
2191
- 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);
2192
2417
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "returned fallback", { snapshot: this.getSnapshot() });
2193
2418
  return;
2194
2419
  }
2195
2420
  const media = normalizeMedia(configured);
2196
2421
  this.desiredMedia = media;
2422
+ this.desiredMediaRole = "primary";
2197
2423
  if (this.paused) {
2198
2424
  this.phase = "idle";
2199
2425
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.show", "deferred media while paused", { media });
@@ -2244,6 +2470,7 @@ var DefaultDrivingExpressionPlayer = class {
2244
2470
  this.container.replaceChildren();
2245
2471
  this.status = null;
2246
2472
  this.desiredMedia = null;
2473
+ this.desiredMediaRole = null;
2247
2474
  this.phase = "idle";
2248
2475
  this.mediaKind = null;
2249
2476
  this.errorCategory = null;
@@ -2312,7 +2539,12 @@ var DefaultDrivingExpressionPlayer = class {
2312
2539
  }
2313
2540
  if (this.status !== null && this.desiredMedia !== null) {
2314
2541
  const operation = this.beginOperation();
2315
- 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);
2316
2548
  }
2317
2549
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.updatePauseState", "returned", this.getSnapshot());
2318
2550
  }
@@ -2375,7 +2607,7 @@ var DefaultDrivingExpressionPlayer = class {
2375
2607
  error,
2376
2608
  operation
2377
2609
  });
2378
- if (!this.isCurrent(operation) || isAbortError(error)) {
2610
+ if (!this.isCurrent(operation) || isAbortError(error, IS_ABORT_ERROR_DEBUG)) {
2379
2611
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.load", "ignored error");
2380
2612
  return;
2381
2613
  }
@@ -2596,19 +2828,37 @@ var DefaultDrivingExpressionPlayer = class {
2596
2828
  status,
2597
2829
  report
2598
2830
  });
2599
- const fallback = this.fallbacks?.[status];
2600
- if (!fallback) {
2831
+ const fallback = this.resolveFallback(status);
2832
+ if (fallback.type !== "static") {
2601
2833
  this.container.replaceChildren();
2602
- dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", { result: false });
2834
+ dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
2835
+ result: false,
2836
+ fallbackType: fallback.type
2837
+ });
2603
2838
  return false;
2604
2839
  }
2605
- this.renderFallback(status, fallback, report);
2840
+ this.renderFallback(status, fallback.media, report);
2606
2841
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderConfiguredFallback", "returned", {
2607
2842
  result: true,
2608
- fallback
2843
+ fallback: fallback.media
2609
2844
  });
2610
2845
  return true;
2611
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
+ }
2612
2862
  renderFallback(status, fallback, report = true) {
2613
2863
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.renderFallback", "called", {
2614
2864
  status,
@@ -2621,11 +2871,11 @@ var DefaultDrivingExpressionPlayer = class {
2621
2871
  this.errorCategory = null;
2622
2872
  }
2623
2873
  if (fallback.kind === "emoji") this.renderEmoji(fallback.text);
2624
- else if (fallback.kind === "image") {
2874
+ else {
2625
2875
  const image = this.createImage();
2626
2876
  image.src = fallback.url;
2627
2877
  this.container.replaceChildren(image);
2628
- } else this.container.replaceChildren();
2878
+ }
2629
2879
  if (report) this.emitDiagnostic({
2630
2880
  type: "media_fallback_applied",
2631
2881
  status,
@@ -2674,14 +2924,15 @@ var DefaultDrivingExpressionPlayer = class {
2674
2924
  });
2675
2925
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.markReady", "returned", this.getSnapshot());
2676
2926
  }
2677
- handleFailure(status, media, error, category, operation, scheduleRetry = true) {
2927
+ handleFailure(status, media, error, category, operation, scheduleRetry = true, applyFallback = true) {
2678
2928
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.handleFailure", "called", {
2679
2929
  status,
2680
2930
  media,
2681
2931
  error,
2682
2932
  category,
2683
2933
  operation,
2684
- scheduleRetry
2934
+ scheduleRetry,
2935
+ applyFallback
2685
2936
  });
2686
2937
  if (!this.isCurrent(operation) || this.failedOperation === operation) {
2687
2938
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.handleFailure", "ignored");
@@ -2690,6 +2941,7 @@ var DefaultDrivingExpressionPlayer = class {
2690
2941
  this.failedOperation = operation;
2691
2942
  this.stopActiveVideo();
2692
2943
  this.phase = "error";
2944
+ if (!applyFallback && media !== null) this.mediaKind = media.kind;
2693
2945
  this.errorCategory = category;
2694
2946
  this.emitDiagnostic({
2695
2947
  type: "media_load_failed",
@@ -2698,8 +2950,24 @@ var DefaultDrivingExpressionPlayer = class {
2698
2950
  category,
2699
2951
  message: safeErrorMessage(error)
2700
2952
  });
2701
- const fallbackApplied = this.renderConfiguredFallback(status);
2702
- 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 {
2703
2971
  this.phase = "error";
2704
2972
  this.errorCategory = category;
2705
2973
  }
@@ -2945,12 +3213,9 @@ var DefaultDrivingExpressionPlayer = class {
2945
3213
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned no listener");
2946
3214
  return;
2947
3215
  }
2948
- try {
2949
- this.onDiagnostic(event);
2950
- dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned", { event });
2951
- } catch (error) {
3216
+ if (invokeSafely(this.onDiagnostic, [event], (error) => {
2952
3217
  dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "listener threw", error);
2953
- }
3218
+ })) dialSdkDebugWarn(DEBUG_MODULE, "DefaultDrivingExpressionPlayer.emitDiagnostic", "returned", { event });
2954
3219
  }
2955
3220
  };
2956
3221
  var MediaPlayerError = class extends Error {
@@ -2983,6 +3248,8 @@ const normalizeMedia = (media) => {
2983
3248
  });
2984
3249
  return result;
2985
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`);
2986
3253
  const isRetainableImage = (media) => {
2987
3254
  if (media.mimeType?.split(";", 1)[0]?.trim().toLowerCase() === "image/gif") return false;
2988
3255
  try {
@@ -3068,7 +3335,7 @@ const waitForElementEvent = (element, successEvent, failureEvent, signal, timeou
3068
3335
  const handleAbort = () => {
3069
3336
  dialSdkDebugWarn(DEBUG_MODULE, "waitForElementEvent.handleAbort", "called");
3070
3337
  cleanup();
3071
- reject(createAbortError());
3338
+ reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
3072
3339
  };
3073
3340
  element.addEventListener(successEvent, handleSuccess, { once: true });
3074
3341
  element.addEventListener(failureEvent, handleFailure, { once: true });
@@ -3105,7 +3372,7 @@ const withTimeout = (promise, timeoutMs, signal) => {
3105
3372
  const handleAbort = () => {
3106
3373
  dialSdkDebugWarn(DEBUG_MODULE, "withTimeout.handleAbort", "called");
3107
3374
  cleanup();
3108
- reject(createAbortError());
3375
+ reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
3109
3376
  };
3110
3377
  signal.addEventListener("abort", handleAbort, { once: true });
3111
3378
  timer = globalThis.setTimeout(() => {
@@ -3132,9 +3399,9 @@ const defaultTgsRenderer = async (container, animationData, signal) => {
3132
3399
  animationData,
3133
3400
  signal
3134
3401
  });
3135
- if (signal.aborted) throw createAbortError();
3402
+ if (signal.aborted) throw createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG);
3136
3403
  const module = await import("lottie-web/build/player/lottie_light.js");
3137
- if (signal.aborted) throw createAbortError();
3404
+ if (signal.aborted) throw createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG);
3138
3405
  const animation = module.default.loadAnimation({
3139
3406
  animationData,
3140
3407
  autoplay: true,
@@ -3158,7 +3425,7 @@ const defaultTgsRenderer = async (container, animationData, signal) => {
3158
3425
  dialSdkDebugWarn(DEBUG_MODULE, "defaultTgsRenderer.aborted", "called");
3159
3426
  cleanup();
3160
3427
  animation.destroy();
3161
- reject(createAbortError());
3428
+ reject(createAbortError("Aborted", CREATE_ABORT_ERROR_DEBUG));
3162
3429
  };
3163
3430
  const cleanup = () => {
3164
3431
  dialSdkDebugWarn(DEBUG_MODULE, "defaultTgsRenderer.cleanup", "called");
@@ -3197,7 +3464,7 @@ function validateTgsAnimation(value, maxLayers) {
3197
3464
  value,
3198
3465
  maxLayers
3199
3466
  });
3200
- 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");
3201
3468
  if (value.layers.length > maxLayers) throw new MediaPlayerError("decode", "TGS animation has too many layers");
3202
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");
3203
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");
@@ -3252,11 +3519,7 @@ const resetVideo = (video) => {
3252
3519
  video.pause();
3253
3520
  video.removeAttribute("src");
3254
3521
  video.removeAttribute("type");
3255
- try {
3256
- video.load();
3257
- } catch (error) {
3258
- dialSdkDebugWarn(DEBUG_MODULE, "resetVideo", "load threw", error);
3259
- }
3522
+ video.load();
3260
3523
  dialSdkDebugWarn(DEBUG_MODULE, "resetVideo", "returned", { video });
3261
3524
  };
3262
3525
  const styleMediaElement = (element, fit) => {
@@ -3303,39 +3566,6 @@ const redactUrlQueries = (message) => {
3303
3566
  dialSdkDebugWarn(DEBUG_MODULE, "redactUrlQueries", "returned", { result });
3304
3567
  return result;
3305
3568
  };
3306
- const createAbortError = () => {
3307
- dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "called");
3308
- if (typeof DOMException !== "undefined") {
3309
- const result = new DOMException("Aborted", "AbortError");
3310
- dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "returned DOMException", { result });
3311
- return result;
3312
- }
3313
- const error = /* @__PURE__ */ new Error("Aborted");
3314
- error.name = "AbortError";
3315
- dialSdkDebugWarn(DEBUG_MODULE, "createAbortError", "returned Error", { result: error });
3316
- return error;
3317
- };
3318
- const isAbortError = (error) => {
3319
- const result = error instanceof Error && error.name === "AbortError";
3320
- dialSdkDebugWarn(DEBUG_MODULE, "isAbortError", "returned", {
3321
- error,
3322
- result
3323
- });
3324
- return result;
3325
- };
3326
- const positiveFinite = (name, value) => {
3327
- dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "called", {
3328
- name,
3329
- value
3330
- });
3331
- if (!Number.isFinite(value) || value <= 0) {
3332
- const error = /* @__PURE__ */ new RangeError(`${name} must be a positive finite number`);
3333
- dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "threw", error);
3334
- throw error;
3335
- }
3336
- dialSdkDebugWarn(DEBUG_MODULE, "positiveFinite", "returned", { result: value });
3337
- return value;
3338
- };
3339
3569
  const safeDestroy = (handle) => {
3340
3570
  dialSdkDebugWarn(DEBUG_MODULE, "safeDestroy", "called", { handle });
3341
3571
  try {
@@ -3345,13 +3575,5 @@ const safeDestroy = (handle) => {
3345
3575
  dialSdkDebugWarn(DEBUG_MODULE, "safeDestroy", "destroy threw", error);
3346
3576
  }
3347
3577
  };
3348
- const isRecord = (value) => {
3349
- const result = typeof value === "object" && value !== null && !Array.isArray(value);
3350
- dialSdkDebugWarn(DEBUG_MODULE, "isRecord", "returned", {
3351
- value,
3352
- result
3353
- });
3354
- return result;
3355
- };
3356
3578
  //#endregion
3357
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 };