@authme/util 2.8.29 → 2.8.31

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.
Files changed (3) hide show
  1. package/index.cjs +168 -14
  2. package/index.js +168 -14
  3. package/package.json +1 -1
package/index.cjs CHANGED
@@ -27,6 +27,7 @@ var fileSaver = require('file-saver');
27
27
  var JSZip = require('jszip');
28
28
  require('core-js/modules/es.parse-int.js');
29
29
  var Lottie = require('lottie-web');
30
+ require('core-js/modules/es.number.to-fixed.js');
30
31
  require('core-js/modules/es.array.sort.js');
31
32
  require('core-js/modules/es.string.trim.js');
32
33
  require('core-js/modules/es.string.starts-with.js');
@@ -1447,6 +1448,50 @@ const videoConstraintsFactory = (isPC, facingMode) => {
1447
1448
  }
1448
1449
  };
1449
1450
  };
1451
+ /**
1452
+ * 等待camera完全就緒
1453
+ * 用於解決外接camera初始化延遲問題
1454
+ * @param videoTrack - MediaStreamTrack 實例
1455
+ * @param maxWaitMs - 最大等待時間(毫秒)
1456
+ * @returns Promise<boolean> - true表示camera已就緒,false表示超時
1457
+ */
1458
+ function waitForCameraReady(videoTrack, maxWaitMs = 100) {
1459
+ var _a, _b, _c, _d;
1460
+ return __awaiter(this, void 0, void 0, function* () {
1461
+ const startTime = Date.now();
1462
+ const checkInterval = 50; // 每50ms檢查一次
1463
+ const minStableTime = 50; // 最小穩定時間:即使檢測到就緒,也要等待一小段時間
1464
+ while (Date.now() - startTime < maxWaitMs) {
1465
+ const _settings = videoTrack.getSettings();
1466
+ const capabilities = (_a = videoTrack.getCapabilities) === null || _a === void 0 ? void 0 : _a.call(videoTrack);
1467
+ // 檢查camera是否已提供完整資訊
1468
+ // 1. 必須有基本的寬高設定
1469
+ // 2. 如果支援 getCapabilities,確認有最大解析度資訊
1470
+ const hasBasicSettings = _settings.width && _settings.height;
1471
+ const hasCapabilities = !capabilities || ((_b = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _b === void 0 ? void 0 : _b.max) && ((_c = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _c === void 0 ? void 0 : _c.max);
1472
+ if (hasBasicSettings && hasCapabilities) {
1473
+ const elapsedTime = Date.now() - startTime;
1474
+ // ⚠️ 關鍵修正:即使檢測到就緒,也要確保經過最小等待時間
1475
+ // 這對外接相機特別重要,它們需要額外的硬體初始化時間
1476
+ if (elapsedTime < minStableTime) {
1477
+ const remainingTime = minStableTime - elapsedTime;
1478
+ console.log(`⏳ Camera基本就緒,額外等待 ${remainingTime}ms 確保穩定 (當前: ${_settings.width}x${_settings.height})`);
1479
+ yield new Promise(resolve => setTimeout(resolve, remainingTime));
1480
+ }
1481
+ console.log(`✅ Camera就緒 (耗時: ${Date.now() - startTime}ms, 解析度: ${_settings.width}x${_settings.height})`);
1482
+ return true;
1483
+ }
1484
+ yield new Promise(resolve => setTimeout(resolve, checkInterval));
1485
+ }
1486
+ const settings = videoTrack.getSettings();
1487
+ console.warn(`⚠️ Camera未在時限內完全就緒 (${maxWaitMs}ms), 當前狀態:`, {
1488
+ width: settings.width,
1489
+ height: settings.height,
1490
+ hasCapabilities: !!((_d = videoTrack.getCapabilities) === null || _d === void 0 ? void 0 : _d.call(videoTrack))
1491
+ });
1492
+ return false;
1493
+ });
1494
+ }
1450
1495
  function inferFacingModeFromLabel(label) {
1451
1496
  const pattern = /rear|back|rück|arrière|trasera|trás|traseira|posteriore|后|後|背|задней|الخلفية|후|arka|achterzijde|หลัง|baksidan|bagside|sau|bak|tylny|takakamera|belakang|אחורית|πίσω|spate|hátsó|zadní|darrere|zadná|задня|stražnja|belakang|बैक/i;
1452
1497
  if (pattern.test(label !== null && label !== void 0 ? label : '')) {
@@ -1501,7 +1546,9 @@ function arrayFromAsync(asyncIterable) {
1501
1546
  });
1502
1547
  }
1503
1548
  function switchCamera(deviceId, video) {
1549
+ var _a, _b, _c, _d;
1504
1550
  return __awaiter(this, void 0, void 0, function* () {
1551
+ const startTime = performance.now();
1505
1552
  try {
1506
1553
  if (stream) {
1507
1554
  stream.getTracks().forEach(track => track.stop());
@@ -1515,13 +1562,15 @@ function switchCamera(deviceId, video) {
1515
1562
  };
1516
1563
  const constraint = localStorage.getItem('camera_constraint');
1517
1564
  stream = yield navigator.mediaDevices.getUserMedia(basicConstraints);
1518
- // 第二階段:如果沒有設定跳過約束,嘗試使用 applyConstraints 提升解析度
1565
+ // 第二階段:等待就緒策略 + 漸進式重試提升解析度
1519
1566
  if (!constraint && stream) {
1520
1567
  const videoTrack = stream.getVideoTracks()[0];
1521
1568
  if (videoTrack && videoTrack.applyConstraints) {
1522
- try {
1523
- // 嘗試應用更高解析度(使用 ideal 而非 min/max)
1524
- yield videoTrack.applyConstraints({
1569
+ // 🆕 策略 1: 等待camera完全就緒(特別針對外接camera)
1570
+ const isReady = yield waitForCameraReady(videoTrack, 2000);
1571
+ if (isReady) {
1572
+ // 🆕 策略 2: 漸進式重試邏輯 + 解析度驗證
1573
+ const targetConstraints = {
1525
1574
  width: {
1526
1575
  ideal: 1920,
1527
1576
  max: 3840
@@ -1530,10 +1579,86 @@ function switchCamera(deviceId, video) {
1530
1579
  ideal: 1080,
1531
1580
  max: 2160
1532
1581
  }
1533
- });
1534
- } catch (error) {
1535
- // 失敗時保持原有流,不會中斷相機
1582
+ };
1583
+ const maxRetries = 3;
1584
+ const delays = [0, 100, 300]; // 第一次立即嘗試,後續間隔遞增
1585
+ const minAcceptableWidth = 1280; // 最低可接受的解析度寬度
1586
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1587
+ try {
1588
+ if (delays[attempt] > 0) {
1589
+ yield new Promise(resolve => setTimeout(resolve, delays[attempt]));
1590
+ }
1591
+ const beforeSettings = videoTrack.getSettings();
1592
+ yield videoTrack.applyConstraints(targetConstraints);
1593
+ // ⚠️ 關鍵:驗證解析度是否真的改變
1594
+ const afterSettings = videoTrack.getSettings();
1595
+ const afterWidth = (_a = afterSettings.width) !== null && _a !== void 0 ? _a : 0;
1596
+ const beforeWidth = (_b = beforeSettings.width) !== null && _b !== void 0 ? _b : 0;
1597
+ // 成功條件:
1598
+ // 1. 如果初始解析度 < 最低標準 → 必須提升且達到最低標準
1599
+ // 2. 如果初始解析度 >= 最低標準 → 只要有提升就算成功
1600
+ const needsImprovement = beforeWidth < minAcceptableWidth;
1601
+ const resolutionImproved = needsImprovement ? afterWidth > beforeWidth && afterWidth >= minAcceptableWidth : afterWidth > beforeWidth;
1602
+ if (resolutionImproved) {
1603
+ console.log(`✅ 解析度提升成功 (嘗試 ${attempt + 1}/${maxRetries})`, {
1604
+ before: `${beforeWidth}x${beforeSettings.height}`,
1605
+ after: `${afterWidth}x${afterSettings.height}`,
1606
+ deviceLabel: videoTrack.label
1607
+ });
1608
+ break; // 真正成功才跳出
1609
+ } else {
1610
+ // applyConstraints 沒報錯,但解析度沒變 → 視為失敗,觸發重試
1611
+ console.warn(`⚠️ 解析度未改善 (嘗試 ${attempt + 1}/${maxRetries})`, {
1612
+ before: `${beforeWidth}x${beforeSettings.height}`,
1613
+ after: `${afterWidth}x${afterSettings.height}`,
1614
+ expected: needsImprovement ? `>= ${minAcceptableWidth}` : '任何提升'
1615
+ });
1616
+ if (attempt === maxRetries - 1) {
1617
+ console.warn(`⚠️ 達到最大重試次數,使用當前解析度 (${afterWidth}x${afterSettings.height})`);
1618
+ } else {
1619
+ // 繼續重試
1620
+ continue;
1621
+ }
1622
+ }
1623
+ } catch (error) {
1624
+ if (attempt === maxRetries - 1) {
1625
+ // 最後一次嘗試失敗,記錄警告但不影響功能
1626
+ const settings = videoTrack.getSettings();
1627
+ console.warn(`⚠️ 無法提升解析度,使用基本設定 (${settings.width}x${settings.height})`, error);
1628
+ } else {
1629
+ console.log(`⏳ 解析度提升嘗試 ${attempt + 1} 失敗,準備重試...`, error);
1630
+ }
1631
+ }
1632
+ }
1633
+ } else {
1634
+ // Camera未完全就緒,但仍嘗試套用約束
1635
+ console.warn('⚠️ Camera未完全就緒,嘗試套用解析度約束...');
1636
+ try {
1637
+ yield videoTrack.applyConstraints({
1638
+ width: {
1639
+ ideal: 1920,
1640
+ max: 3840
1641
+ },
1642
+ height: {
1643
+ ideal: 1080,
1644
+ max: 2160
1645
+ }
1646
+ });
1647
+ } catch (error) {
1648
+ console.warn('⚠️ 解析度提升失敗,使用預設解析度', error);
1649
+ }
1536
1650
  }
1651
+ // 🆕 記錄最終解析度資訊
1652
+ const finalSettings = videoTrack.getSettings();
1653
+ const initTime = (performance.now() - startTime).toFixed(2);
1654
+ console.log('📹 Camera初始化完成', {
1655
+ deviceId,
1656
+ deviceLabel: videoTrack.label,
1657
+ resolution: `${finalSettings.width}x${finalSettings.height}`,
1658
+ frameRate: finalSettings.frameRate,
1659
+ facingMode: finalSettings.facingMode,
1660
+ initTime: `${initTime}ms`
1661
+ });
1537
1662
  }
1538
1663
  }
1539
1664
  // try {
@@ -1579,12 +1704,28 @@ function switchCamera(deviceId, video) {
1579
1704
  video.srcObject = stream;
1580
1705
  }, 10);
1581
1706
  } catch (e) {
1707
+ // 🆕 增強錯誤日誌
1708
+ const initTime = (performance.now() - startTime).toFixed(2);
1709
+ console.error('❌ Camera切換失敗', {
1710
+ deviceId,
1711
+ error: e,
1712
+ errorName: e === null || e === void 0 ? void 0 : e.name,
1713
+ errorMessage: e === null || e === void 0 ? void 0 : e.message,
1714
+ initTime: `${initTime}ms`,
1715
+ streamExists: !!stream,
1716
+ trackCount: (_d = (_c = stream === null || stream === void 0 ? void 0 : stream.getTracks()) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0
1717
+ });
1582
1718
  throw new AuthmeError(exports.ErrorCode.CAMERA_NOT_SUPPORT, e);
1583
1719
  }
1584
1720
  });
1585
1721
  }
1586
1722
  function _requestCamera(video, facingMode) {
1723
+ var _a, _b, _c, _d, _e, _f;
1587
1724
  return __awaiter(this, void 0, void 0, function* () {
1725
+ const requestStartTime = performance.now();
1726
+ console.log('🎬 開始camera請求流程', {
1727
+ facingMode
1728
+ });
1588
1729
  if (!('mediaDevices' in navigator)) {
1589
1730
  throw BROWSER_CAMERA_ERRORS.NOT_SUPPORT;
1590
1731
  }
@@ -1607,7 +1748,7 @@ function _requestCamera(video, facingMode) {
1607
1748
  // 輪詢取得攝影機 解析度等資訊
1608
1749
  const videoDevices = (yield navigator.mediaDevices.enumerateDevices()).filter(device => device.kind === 'videoinput');
1609
1750
  const deviceMetas = yield arrayFromAsync(asyncMap(device => __awaiter(this, void 0, void 0, function* () {
1610
- var _a, _b, _c, _d;
1751
+ var _g, _h, _j, _k;
1611
1752
  const stream = yield navigator.mediaDevices.getUserMedia({
1612
1753
  video: {
1613
1754
  deviceId: {
@@ -1615,10 +1756,10 @@ function _requestCamera(video, facingMode) {
1615
1756
  }
1616
1757
  }
1617
1758
  });
1618
- const track = (_a = stream.getVideoTracks()) === null || _a === void 0 ? void 0 : _a[0];
1619
- const capabilities = ((_b = track === null || track === void 0 ? void 0 : track.getCapabilities) !== null && _b !== void 0 ? _b : () => undefined).bind(track)(); // firefox 沒有 getCapabilities 可以用。
1620
- const widthMax = (_c = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _c === void 0 ? void 0 : _c.max;
1621
- const heightMax = (_d = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _d === void 0 ? void 0 : _d.max;
1759
+ const track = (_g = stream.getVideoTracks()) === null || _g === void 0 ? void 0 : _g[0];
1760
+ const capabilities = ((_h = track === null || track === void 0 ? void 0 : track.getCapabilities) !== null && _h !== void 0 ? _h : () => undefined).bind(track)(); // firefox 沒有 getCapabilities 可以用。
1761
+ const widthMax = (_j = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _j === void 0 ? void 0 : _j.max;
1762
+ const heightMax = (_k = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _k === void 0 ? void 0 : _k.max;
1622
1763
  const resolution = widthMax && heightMax ? widthMax * heightMax : 0;
1623
1764
  const facingModeFromLabel = inferFacingModeFromLabel(device === null || device === void 0 ? void 0 : device.label);
1624
1765
  const facingModeFromapabilities = inferFacingModeFromCapabilities(capabilities);
@@ -1678,6 +1819,19 @@ function _requestCamera(video, facingMode) {
1678
1819
  stream.getTracks().forEach(track => track.stop());
1679
1820
  }
1680
1821
  yield switchCamera(deviceId, video);
1822
+ // 🆕 記錄整個請求流程的總結資訊
1823
+ const totalTime = (performance.now() - requestStartTime).toFixed(2);
1824
+ console.log('🎉 Camera請求流程完成', {
1825
+ totalTime: `${totalTime}ms`,
1826
+ selectedDevice: {
1827
+ deviceId,
1828
+ label: firstDevice.device.label,
1829
+ facingMode: firstDevice.meta.facingMode,
1830
+ resolution: `${(_c = (_b = (_a = firstDevice.meta.capabilities) === null || _a === void 0 ? void 0 : _a.width) === null || _b === void 0 ? void 0 : _b.max) !== null && _c !== void 0 ? _c : 0}x${(_f = (_e = (_d = firstDevice.meta.capabilities) === null || _d === void 0 ? void 0 : _d.height) === null || _e === void 0 ? void 0 : _e.max) !== null && _f !== void 0 ? _f : 0}`
1831
+ },
1832
+ availableDevices: deviceMetas.length,
1833
+ isSamsung
1834
+ });
1681
1835
  return {
1682
1836
  facingMode: firstDevice.meta.facingMode,
1683
1837
  deviceMetas: deviceMetas
@@ -3243,8 +3397,8 @@ const themeConfigDefault = {
3243
3397
  };
3244
3398
 
3245
3399
  var name = "authme/sdk";
3246
- var version$1 = "2.8.29";
3247
- var date = "2025-10-30T04:42:12+0000";
3400
+ var version$1 = "2.8.31";
3401
+ var date = "2025-11-05T04:02:33+0000";
3248
3402
  var packageInfo = {
3249
3403
  name: name,
3250
3404
  version: version$1,
package/index.js CHANGED
@@ -23,6 +23,7 @@ import { saveAs } from 'file-saver';
23
23
  import JSZip from 'jszip';
24
24
  import 'core-js/modules/es.parse-int.js';
25
25
  import Lottie from 'lottie-web';
26
+ import 'core-js/modules/es.number.to-fixed.js';
26
27
  import 'core-js/modules/es.array.sort.js';
27
28
  import 'core-js/modules/es.string.trim.js';
28
29
  import 'core-js/modules/es.string.starts-with.js';
@@ -1437,6 +1438,50 @@ const videoConstraintsFactory = (isPC, facingMode) => {
1437
1438
  }
1438
1439
  };
1439
1440
  };
1441
+ /**
1442
+ * 等待camera完全就緒
1443
+ * 用於解決外接camera初始化延遲問題
1444
+ * @param videoTrack - MediaStreamTrack 實例
1445
+ * @param maxWaitMs - 最大等待時間(毫秒)
1446
+ * @returns Promise<boolean> - true表示camera已就緒,false表示超時
1447
+ */
1448
+ function waitForCameraReady(videoTrack, maxWaitMs = 100) {
1449
+ var _a, _b, _c, _d;
1450
+ return __awaiter(this, void 0, void 0, function* () {
1451
+ const startTime = Date.now();
1452
+ const checkInterval = 50; // 每50ms檢查一次
1453
+ const minStableTime = 50; // 最小穩定時間:即使檢測到就緒,也要等待一小段時間
1454
+ while (Date.now() - startTime < maxWaitMs) {
1455
+ const _settings = videoTrack.getSettings();
1456
+ const capabilities = (_a = videoTrack.getCapabilities) === null || _a === void 0 ? void 0 : _a.call(videoTrack);
1457
+ // 檢查camera是否已提供完整資訊
1458
+ // 1. 必須有基本的寬高設定
1459
+ // 2. 如果支援 getCapabilities,確認有最大解析度資訊
1460
+ const hasBasicSettings = _settings.width && _settings.height;
1461
+ const hasCapabilities = !capabilities || ((_b = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _b === void 0 ? void 0 : _b.max) && ((_c = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _c === void 0 ? void 0 : _c.max);
1462
+ if (hasBasicSettings && hasCapabilities) {
1463
+ const elapsedTime = Date.now() - startTime;
1464
+ // ⚠️ 關鍵修正:即使檢測到就緒,也要確保經過最小等待時間
1465
+ // 這對外接相機特別重要,它們需要額外的硬體初始化時間
1466
+ if (elapsedTime < minStableTime) {
1467
+ const remainingTime = minStableTime - elapsedTime;
1468
+ console.log(`⏳ Camera基本就緒,額外等待 ${remainingTime}ms 確保穩定 (當前: ${_settings.width}x${_settings.height})`);
1469
+ yield new Promise(resolve => setTimeout(resolve, remainingTime));
1470
+ }
1471
+ console.log(`✅ Camera就緒 (耗時: ${Date.now() - startTime}ms, 解析度: ${_settings.width}x${_settings.height})`);
1472
+ return true;
1473
+ }
1474
+ yield new Promise(resolve => setTimeout(resolve, checkInterval));
1475
+ }
1476
+ const settings = videoTrack.getSettings();
1477
+ console.warn(`⚠️ Camera未在時限內完全就緒 (${maxWaitMs}ms), 當前狀態:`, {
1478
+ width: settings.width,
1479
+ height: settings.height,
1480
+ hasCapabilities: !!((_d = videoTrack.getCapabilities) === null || _d === void 0 ? void 0 : _d.call(videoTrack))
1481
+ });
1482
+ return false;
1483
+ });
1484
+ }
1440
1485
  function inferFacingModeFromLabel(label) {
1441
1486
  const pattern = /rear|back|rück|arrière|trasera|trás|traseira|posteriore|后|後|背|задней|الخلفية|후|arka|achterzijde|หลัง|baksidan|bagside|sau|bak|tylny|takakamera|belakang|אחורית|πίσω|spate|hátsó|zadní|darrere|zadná|задня|stražnja|belakang|बैक/i;
1442
1487
  if (pattern.test(label !== null && label !== void 0 ? label : '')) {
@@ -1491,7 +1536,9 @@ function arrayFromAsync(asyncIterable) {
1491
1536
  });
1492
1537
  }
1493
1538
  function switchCamera(deviceId, video) {
1539
+ var _a, _b, _c, _d;
1494
1540
  return __awaiter(this, void 0, void 0, function* () {
1541
+ const startTime = performance.now();
1495
1542
  try {
1496
1543
  if (stream) {
1497
1544
  stream.getTracks().forEach(track => track.stop());
@@ -1505,13 +1552,15 @@ function switchCamera(deviceId, video) {
1505
1552
  };
1506
1553
  const constraint = localStorage.getItem('camera_constraint');
1507
1554
  stream = yield navigator.mediaDevices.getUserMedia(basicConstraints);
1508
- // 第二階段:如果沒有設定跳過約束,嘗試使用 applyConstraints 提升解析度
1555
+ // 第二階段:等待就緒策略 + 漸進式重試提升解析度
1509
1556
  if (!constraint && stream) {
1510
1557
  const videoTrack = stream.getVideoTracks()[0];
1511
1558
  if (videoTrack && videoTrack.applyConstraints) {
1512
- try {
1513
- // 嘗試應用更高解析度(使用 ideal 而非 min/max)
1514
- yield videoTrack.applyConstraints({
1559
+ // 🆕 策略 1: 等待camera完全就緒(特別針對外接camera)
1560
+ const isReady = yield waitForCameraReady(videoTrack, 2000);
1561
+ if (isReady) {
1562
+ // 🆕 策略 2: 漸進式重試邏輯 + 解析度驗證
1563
+ const targetConstraints = {
1515
1564
  width: {
1516
1565
  ideal: 1920,
1517
1566
  max: 3840
@@ -1520,10 +1569,86 @@ function switchCamera(deviceId, video) {
1520
1569
  ideal: 1080,
1521
1570
  max: 2160
1522
1571
  }
1523
- });
1524
- } catch (error) {
1525
- // 失敗時保持原有流,不會中斷相機
1572
+ };
1573
+ const maxRetries = 3;
1574
+ const delays = [0, 100, 300]; // 第一次立即嘗試,後續間隔遞增
1575
+ const minAcceptableWidth = 1280; // 最低可接受的解析度寬度
1576
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1577
+ try {
1578
+ if (delays[attempt] > 0) {
1579
+ yield new Promise(resolve => setTimeout(resolve, delays[attempt]));
1580
+ }
1581
+ const beforeSettings = videoTrack.getSettings();
1582
+ yield videoTrack.applyConstraints(targetConstraints);
1583
+ // ⚠️ 關鍵:驗證解析度是否真的改變
1584
+ const afterSettings = videoTrack.getSettings();
1585
+ const afterWidth = (_a = afterSettings.width) !== null && _a !== void 0 ? _a : 0;
1586
+ const beforeWidth = (_b = beforeSettings.width) !== null && _b !== void 0 ? _b : 0;
1587
+ // 成功條件:
1588
+ // 1. 如果初始解析度 < 最低標準 → 必須提升且達到最低標準
1589
+ // 2. 如果初始解析度 >= 最低標準 → 只要有提升就算成功
1590
+ const needsImprovement = beforeWidth < minAcceptableWidth;
1591
+ const resolutionImproved = needsImprovement ? afterWidth > beforeWidth && afterWidth >= minAcceptableWidth : afterWidth > beforeWidth;
1592
+ if (resolutionImproved) {
1593
+ console.log(`✅ 解析度提升成功 (嘗試 ${attempt + 1}/${maxRetries})`, {
1594
+ before: `${beforeWidth}x${beforeSettings.height}`,
1595
+ after: `${afterWidth}x${afterSettings.height}`,
1596
+ deviceLabel: videoTrack.label
1597
+ });
1598
+ break; // 真正成功才跳出
1599
+ } else {
1600
+ // applyConstraints 沒報錯,但解析度沒變 → 視為失敗,觸發重試
1601
+ console.warn(`⚠️ 解析度未改善 (嘗試 ${attempt + 1}/${maxRetries})`, {
1602
+ before: `${beforeWidth}x${beforeSettings.height}`,
1603
+ after: `${afterWidth}x${afterSettings.height}`,
1604
+ expected: needsImprovement ? `>= ${minAcceptableWidth}` : '任何提升'
1605
+ });
1606
+ if (attempt === maxRetries - 1) {
1607
+ console.warn(`⚠️ 達到最大重試次數,使用當前解析度 (${afterWidth}x${afterSettings.height})`);
1608
+ } else {
1609
+ // 繼續重試
1610
+ continue;
1611
+ }
1612
+ }
1613
+ } catch (error) {
1614
+ if (attempt === maxRetries - 1) {
1615
+ // 最後一次嘗試失敗,記錄警告但不影響功能
1616
+ const settings = videoTrack.getSettings();
1617
+ console.warn(`⚠️ 無法提升解析度,使用基本設定 (${settings.width}x${settings.height})`, error);
1618
+ } else {
1619
+ console.log(`⏳ 解析度提升嘗試 ${attempt + 1} 失敗,準備重試...`, error);
1620
+ }
1621
+ }
1622
+ }
1623
+ } else {
1624
+ // Camera未完全就緒,但仍嘗試套用約束
1625
+ console.warn('⚠️ Camera未完全就緒,嘗試套用解析度約束...');
1626
+ try {
1627
+ yield videoTrack.applyConstraints({
1628
+ width: {
1629
+ ideal: 1920,
1630
+ max: 3840
1631
+ },
1632
+ height: {
1633
+ ideal: 1080,
1634
+ max: 2160
1635
+ }
1636
+ });
1637
+ } catch (error) {
1638
+ console.warn('⚠️ 解析度提升失敗,使用預設解析度', error);
1639
+ }
1526
1640
  }
1641
+ // 🆕 記錄最終解析度資訊
1642
+ const finalSettings = videoTrack.getSettings();
1643
+ const initTime = (performance.now() - startTime).toFixed(2);
1644
+ console.log('📹 Camera初始化完成', {
1645
+ deviceId,
1646
+ deviceLabel: videoTrack.label,
1647
+ resolution: `${finalSettings.width}x${finalSettings.height}`,
1648
+ frameRate: finalSettings.frameRate,
1649
+ facingMode: finalSettings.facingMode,
1650
+ initTime: `${initTime}ms`
1651
+ });
1527
1652
  }
1528
1653
  }
1529
1654
  // try {
@@ -1569,12 +1694,28 @@ function switchCamera(deviceId, video) {
1569
1694
  video.srcObject = stream;
1570
1695
  }, 10);
1571
1696
  } catch (e) {
1697
+ // 🆕 增強錯誤日誌
1698
+ const initTime = (performance.now() - startTime).toFixed(2);
1699
+ console.error('❌ Camera切換失敗', {
1700
+ deviceId,
1701
+ error: e,
1702
+ errorName: e === null || e === void 0 ? void 0 : e.name,
1703
+ errorMessage: e === null || e === void 0 ? void 0 : e.message,
1704
+ initTime: `${initTime}ms`,
1705
+ streamExists: !!stream,
1706
+ trackCount: (_d = (_c = stream === null || stream === void 0 ? void 0 : stream.getTracks()) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0
1707
+ });
1572
1708
  throw new AuthmeError(ErrorCode.CAMERA_NOT_SUPPORT, e);
1573
1709
  }
1574
1710
  });
1575
1711
  }
1576
1712
  function _requestCamera(video, facingMode) {
1713
+ var _a, _b, _c, _d, _e, _f;
1577
1714
  return __awaiter(this, void 0, void 0, function* () {
1715
+ const requestStartTime = performance.now();
1716
+ console.log('🎬 開始camera請求流程', {
1717
+ facingMode
1718
+ });
1578
1719
  if (!('mediaDevices' in navigator)) {
1579
1720
  throw BROWSER_CAMERA_ERRORS.NOT_SUPPORT;
1580
1721
  }
@@ -1597,7 +1738,7 @@ function _requestCamera(video, facingMode) {
1597
1738
  // 輪詢取得攝影機 解析度等資訊
1598
1739
  const videoDevices = (yield navigator.mediaDevices.enumerateDevices()).filter(device => device.kind === 'videoinput');
1599
1740
  const deviceMetas = yield arrayFromAsync(asyncMap(device => __awaiter(this, void 0, void 0, function* () {
1600
- var _a, _b, _c, _d;
1741
+ var _g, _h, _j, _k;
1601
1742
  const stream = yield navigator.mediaDevices.getUserMedia({
1602
1743
  video: {
1603
1744
  deviceId: {
@@ -1605,10 +1746,10 @@ function _requestCamera(video, facingMode) {
1605
1746
  }
1606
1747
  }
1607
1748
  });
1608
- const track = (_a = stream.getVideoTracks()) === null || _a === void 0 ? void 0 : _a[0];
1609
- const capabilities = ((_b = track === null || track === void 0 ? void 0 : track.getCapabilities) !== null && _b !== void 0 ? _b : () => undefined).bind(track)(); // firefox 沒有 getCapabilities 可以用。
1610
- const widthMax = (_c = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _c === void 0 ? void 0 : _c.max;
1611
- const heightMax = (_d = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _d === void 0 ? void 0 : _d.max;
1749
+ const track = (_g = stream.getVideoTracks()) === null || _g === void 0 ? void 0 : _g[0];
1750
+ const capabilities = ((_h = track === null || track === void 0 ? void 0 : track.getCapabilities) !== null && _h !== void 0 ? _h : () => undefined).bind(track)(); // firefox 沒有 getCapabilities 可以用。
1751
+ const widthMax = (_j = capabilities === null || capabilities === void 0 ? void 0 : capabilities.width) === null || _j === void 0 ? void 0 : _j.max;
1752
+ const heightMax = (_k = capabilities === null || capabilities === void 0 ? void 0 : capabilities.height) === null || _k === void 0 ? void 0 : _k.max;
1612
1753
  const resolution = widthMax && heightMax ? widthMax * heightMax : 0;
1613
1754
  const facingModeFromLabel = inferFacingModeFromLabel(device === null || device === void 0 ? void 0 : device.label);
1614
1755
  const facingModeFromapabilities = inferFacingModeFromCapabilities(capabilities);
@@ -1668,6 +1809,19 @@ function _requestCamera(video, facingMode) {
1668
1809
  stream.getTracks().forEach(track => track.stop());
1669
1810
  }
1670
1811
  yield switchCamera(deviceId, video);
1812
+ // 🆕 記錄整個請求流程的總結資訊
1813
+ const totalTime = (performance.now() - requestStartTime).toFixed(2);
1814
+ console.log('🎉 Camera請求流程完成', {
1815
+ totalTime: `${totalTime}ms`,
1816
+ selectedDevice: {
1817
+ deviceId,
1818
+ label: firstDevice.device.label,
1819
+ facingMode: firstDevice.meta.facingMode,
1820
+ resolution: `${(_c = (_b = (_a = firstDevice.meta.capabilities) === null || _a === void 0 ? void 0 : _a.width) === null || _b === void 0 ? void 0 : _b.max) !== null && _c !== void 0 ? _c : 0}x${(_f = (_e = (_d = firstDevice.meta.capabilities) === null || _d === void 0 ? void 0 : _d.height) === null || _e === void 0 ? void 0 : _e.max) !== null && _f !== void 0 ? _f : 0}`
1821
+ },
1822
+ availableDevices: deviceMetas.length,
1823
+ isSamsung
1824
+ });
1671
1825
  return {
1672
1826
  facingMode: firstDevice.meta.facingMode,
1673
1827
  deviceMetas: deviceMetas
@@ -3233,8 +3387,8 @@ const themeConfigDefault = {
3233
3387
  };
3234
3388
 
3235
3389
  var name = "authme/sdk";
3236
- var version$1 = "2.8.29";
3237
- var date = "2025-10-30T04:42:12+0000";
3390
+ var version$1 = "2.8.31";
3391
+ var date = "2025-11-05T04:02:33+0000";
3238
3392
  var packageInfo = {
3239
3393
  name: name,
3240
3394
  version: version$1,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authme/util",
3
- "version": "2.8.29",
3
+ "version": "2.8.31",
4
4
  "peerDependencies": {
5
5
  "core-js": "^3.6.0",
6
6
  "file-saver": "2.0.5",