-
Notifications
You must be signed in to change notification settings - Fork 21
fix: use @stoprocent/noble ble #539
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Caution Review failedThe pull request is closed. WalkthroughThe PR finalizes 1.1.10 release versions, replaces Abandonware Noble with @stoprocent/noble across Electron, adds @onekeyfe/hd-transport-electron, introduces BLE write/subscribe robustness (Windows pairing retries, unified cleanup, soft refresh), updates Electron build configs/scripts, adjusts Expo examples, and refactors CI to OS-specific desktop builds and version-gated publish. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant App as Electron App
participant Main as Main Process
participant Web as BrowserWindow WebContents
participant BLE as Noble (@stoprocent/noble)
participant Dev as BLE Device
App->>Main: app.on('ready')
Main->>Web: createMainWindow()
Main->>Main: setupNobleBleHandlers(Web)
Main->>BLE: noble.startScanning()
BLE-->>Main: discover(peripheral)
Main->>BLE: connect(peripheral)
BLE-->>Main: connected
Main->>BLE: discoverServices/Characteristics
Main->>BLE: subscribe(notify)
BLE-->>Main: data(chunk)
Main->>Main: processNotificationData()
alt complete packet
Main-->>Web: send packet to app layer
else incomplete/error
Main->>Main: accumulate/log
end
sequenceDiagram
autonumber
participant Main as Main Process
participant BLE as Noble
participant Dev as BLE Device
participant Retry as p-retry
participant IPC as IPC
rect rgba(200,230,255,0.3)
note over Main: Windows-specific write flow
Main->>Retry: attemptWindowsWriteUntilPaired()
loop backoff retries
Retry->>BLE: writeCharacteristicWithAck()
alt abortable error (e.g., pairing lost)
Retry-->>Main: throw Abort
Main->>Main: cleanupDevice(deviceId, deep)
Main->>BLE: stopNotifications/unsubscribe
Main->>Main: softRefreshSubscription()
else success
Retry-->>Main: ok
end
end
end
rect rgba(230,200,255,0.25)
note over IPC,Main: Cancel pairing
IPC-->>Main: NOBLE_BLE_CANCEL_PAIRING
Main->>Main: cleanup all devices, stop scanning
Main->>BLE: remove listeners
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro Cache: Disabled due to data retention organization setting Knowledge Base: Disabled due to data retention organization setting 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (33)
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
🎉 Snyk checks have passed. No issues have been found so far.✅ security/snyk check is complete. No issues have been found. (View Details) ✅ license/snyk check is complete. No issues have been found. (View Details) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🔭 Outside diff range comments (9)
packages/hd-transport-electron/src/noble-ble-handler.ts (9)
26-26
: Fix typo in comment“bundlinpissues” → “bundling issues”
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
75-81
: Connection timeout mismatch (comment vs value)Comment says 15 seconds, constant is 3000 ms (3 seconds). This can cause spurious timeouts on BLE connect. Align the value with the comment, or update the comment.
-const CONNECTION_TIMEOUT = 3000; // 15 seconds for device connection +const CONNECTION_TIMEOUT = 15000; // 15 seconds for device connection
388-389
: Syntax error: stray “\n” literals and missing deviceCacheThis line contains literal “\n” sequences in source. That will not compile. It also references deviceCache, which is not defined anywhere.
Option A (quick fix: remove the broken caching block):
- // First check if we have a recent cached peripheral\n const cachedDevice = deviceCache.get(targetDeviceId);\n if (cachedDevice && (Date.now() - cachedDevice.lastSeen) < 30000) { // 30 seconds cache\n logger?.info('[NobleBLE] Using cached device for fast connection:', targetDeviceId);\n \n // Use cached device if it was successful before\n if (cachedDevice.connectionSuccess) {\n discoveredDevices.set(targetDeviceId, cachedDevice.peripheral);\n return cachedDevice.peripheral;\n }\n }\n\n logger?.info('[NobleBLE] Starting targeted scan for device:', targetDeviceId); + logger?.info('[NobleBLE] Starting targeted scan for device:', targetDeviceId);Option B (implement a proper cache): I can provide a full patch introducing a typed deviceCache and wiring updates on discovery and successful connections.
Do you want me to wire up a proper cache implementation?
390-446
: Leak: on('discover') listener not removed on timeoutWhen the targeted scan times out, you stop scanning but never remove the onDiscover handler, which can leak listeners across scans.
const timeout = setTimeout(() => { if (noble) { noble.stopScanning(); + noble.removeListener('discover', onDiscover); } logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); resolve(null); }, FAST_SCAN_TIMEOUT);
431-444
: Callback error handling: handle both string and ErrorNoble historically calls callbacks with string errors. Using error.message can produce undefined. Normalize error values before wrapping in TypedError.
Introduce a helper once and use it everywhere:
function toErrorMessage(err: unknown): string { if (typeof err === 'string') return err; if (err && typeof err === 'object' && 'message' in err) { // eslint-disable-next-line @typescript-eslint/no-base-to-string return (err as any).message ?? String(err); } try { return JSON.stringify(err); } catch { return String(err); } }Then:
- reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error)));Apply the same pattern to all callbacks in this file that use error.message.
481-486
: Callback error handling: handle both string and ErrorSame issue as above. Wrap the error with a normalizer before building a TypedError.
- reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error)));
734-737
: Disconnect listener missing for fresh peripheral pathWhen connectAndDiscoverWithFreshScan connects a freshPeripheral, you don’t register a disconnect listener here (this function doesn’t have webContents). If discovery falls back to this path, disconnect events may not be observed.
Pass webContents into connectAndDiscoverWithFreshScan and hook the listener:
-async function connectAndDiscoverWithFreshScan(deviceId: string): Promise<CharacteristicPair> { +async function connectAndDiscoverWithFreshScan(deviceId: string, webContents: WebContents): Promise<CharacteristicPair> { ... - await new Promise<void>((resolve, reject) => { + await new Promise<void>((resolve, reject) => { freshPeripheral.connect((error: Error | undefined) => { ... connectedDevices.set(deviceId, freshPeripheral); + setupDisconnectListener(freshPeripheral, deviceId, webContents); resolve(); }); });Update the two call sites in connectDevice accordingly.
872-885
: Potential recursion loop on subscription operationsconnectDevice recurses if a subscription operation is in-flight. If that state gets stuck, you can recurse indefinitely. Consider a max retry count or turning this into a loop with a backoff.
1226-1230
: Leftover debug logconsole.log('NOBLE_VERSION_771') looks like a build marker. Gate it behind a dev flag or remove.
- console.log('NOBLE_VERSION_771'); + if (process.env.NODE_ENV !== 'production') console.log('NOBLE_VERSION_771');
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (5)
packages/connect-examples/electron-example/package.json
(1 hunks)packages/connect-examples/electron-example/webpack.config.ts
(1 hunks)packages/hd-transport-electron/package.json
(1 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(11 hunks)packages/hd-transport-electron/src/types/noble-extended.ts
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
packages/hd-transport-electron/src/types/noble-extended.ts (1)
NobleModule
(22-34)packages/shared/src/HardwareError.ts (1)
HardwareErrorCode
(77-459)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: lint (22)
- GitHub Check: build (22)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
packages/hd-transport-electron/package.json (1)
28-30
: Match runtime deps with bundling/externalsGood to see @stoprocent/noble added here. Ensure your bundling/externals setup for packages consuming this code doesn’t strip the native dependency tree that noble needs at runtime (notably the HCI socket). Otherwise, ship them explicitly in this package.json.
packages/hd-transport-electron/src/noble-ble-handler.ts (1)
96-157
: Packet assembly: verify header offsetsYou read payload length at offset 5 (readInt32BE(5)) and subtract COMMON_HEADER_SIZE from current buffer length when checking completeness. Confirm this matches the exact header layout emitted by @onekeyfe/hd-transport, otherwise you can over/under-assemble.
If needed, I can add assertions or structured parsing to validate header fields before assembling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx (1)
568-591
: Map SDK platform to the correct IPlatform value at runtimeHardcoding
'web'
conflicts with the SDK’s union ofnative | desktop | ext | web | web-embed
. UsePlatform.OS
to derive one of these.• packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx (around lines 568–580)
• packages/connect-examples/expo-example/src/data/device.ts (around lines 145, 152)Suggested change:
+ import { Platform } from 'react-native'; + import type { IPlatform } from 'core/src/types/api/firmwareUpdate'; if (type === 'ble' || type === 'firmware') { setShowUpdateDialog(true); - console.log('Starting firmware update:', { type, deviceId: selectDevice.connectId, platform: 'web' }); + // Map RN platform to SDK’s IPlatform + const sdkPlatform: IPlatform = Platform.OS === 'web' + ? 'web' + : ['macos', 'windows'].includes(Platform.OS) + ? 'desktop' + : 'native'; + console.log('Starting firmware update:', { + type, + deviceId: selectDevice.connectId, + platform: sdkPlatform, + }); const deviceId = selectDevice.connectId; console.log('Using device ID for firmware update:', deviceId); - const res = await sdk.firmwareUpdateV2(deviceId, { - binary: fileData, - updateType: type, - platform: 'web', - }); + const res = await sdk.firmwareUpdateV2(deviceId, { + binary: fileData, + updateType: type, + platform: sdkPlatform, + });Also replace other hardcoded
platform: 'web'
entries insrc/data/device.ts
with the same runtime mapping.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
package.json
(1 hunks)packages/connect-examples/expo-example/src/components/DeviceList.tsx
(2 hunks)packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx
(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx (2)
packages/core/src/utils/logger.ts (1)
error
(49-56)packages/connect-examples/expo-example/src/views/FirmwareScreen/MessageBox.tsx (1)
MessageBox
(7-13)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
package.json (1)
52-52
: Dev script verified—approving change
Thepackages/hd-transport-electron/package.json
defines adev
script, soyarn dev:transport-electron
will run as expected.packages/connect-examples/expo-example/src/components/DeviceList.tsx (1)
132-141
: Good: no auto-select for desktop BLE to prevent surprise connects.This change reduces unintended connections and device switching issues. One note: the comment above says WebUSB auth is handled in the SDK, but there’s still a manual
navigator.usb.requestDevice
call earlier. Either remove that call or update the comment to avoid confusion and double prompts.Outside this hunk, consider removing the manual WebUSB request:
// Remove this block if SDK handles permission internally: if (connectionType === 'webusb') { try { await window?.navigator?.usb?.requestDevice({ filters: ONEKEY_WEBUSB_FILTER }); } catch (error) { console.warn('WebUSB request device failed:', error); } }
packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx
Outdated
Show resolved
Hide resolved
packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx
Outdated
Show resolved
Hide resolved
packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
414-424
: Targeted scan: remove 'discover' listener on timeout to prevent leaks.When the timeout fires you stop scanning and resolve(null) but do not remove the onDiscover listener. This can leak handlers across calls.
const timeout = setTimeout(() => { if (noble) { noble.stopScanning(); } logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); - resolve(null); + if (noble) { + noble.removeListener('discover', onDiscover); + } + resolve(null); }, FAST_SCAN_TIMEOUT);
898-911
: Avoid recursive reconnect loop.If operation state stays non-idle,
return connectDevice(...)
recursively chains and can grow stack on repeated retries. Use a loop/poll with a bounded wait instead.- await wait(100); - // Retry connection after waiting - return connectDevice(deviceId, webContents); + for (let i = 0; i < 50; i++) { // 5s max + const s = subscriptionOperations.get(deviceId); + if (!s || s === 'idle') break; + await wait(100); + } + // fall through and continue in the current call
♻️ Duplicate comments (13)
packages/connect-examples/electron-example/package.json (1)
23-30
: Add @stoprocent/bluetooth-hci-socket to avoid Linux runtime failuresNoble needs the HCI socket on Linux. Optional deps often don’t make it into packaged Electron apps. Add it explicitly to prevent “module not found” at runtime.
"dependencies": { "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4", "electron-is-dev": "^3.0.1",
#!/bin/bash set -euo pipefail cd packages/connect-examples/electron-example # Confirm dependency presence and version npm ls @stoprocent/bluetooth-hci-socket || true # Sanity check externals and imports rg -n "@stoprocent/bluetooth-hci-socket" -g "!**/dist/**" -C2 || truepackages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx (3)
410-426
: Race-proof loadOnekeyFeatures by capturing connectIdIf connectId changes mid-flight, you can set stale state later. Capture it and use the captured value during the call.
- const loadOnekeyFeatures = useCallback(async () => { - if (!sdk || !selectDevice?.connectId) return undefined; + const loadOnekeyFeatures = useCallback(async () => { + if (!sdk || !selectDevice?.connectId) return undefined; + const currentId = selectDevice.connectId; try { console.log('loadOnekeyFeatures: Starting to load OneKey features...'); - const res = await sdk.getOnekeyFeatures(selectDevice.connectId); + const res = await sdk.getOnekeyFeatures(currentId); console.log('loadOnekeyFeatures: Result:', res); if (res.success) { return res.payload; } return undefined; } catch (error) { console.error('loadOnekeyFeatures: Error:', error); return undefined; } }, [sdk, selectDevice?.connectId]);
430-468
: Avoid setState after unmount/selection change; normalize errorsAdd a cancel flag and capture connectId. Check both before setting state. Also guard error extraction.
useEffect(() => { if (!sdk) return; if (selectDevice?.connectId == null) { setFeatures(undefined); setOnekeyFeatures(undefined); return; } - const loadDeviceFeatures = async () => { - setConnecting(true); + let cancelled = false; + const currentId = selectDevice.connectId; + + const loadDeviceFeatures = async () => { + if (cancelled) return; + setConnecting(true); setFeatures(undefined); setOnekeyFeatures(undefined); setError(undefined); try { - console.log('Loading device features for:', selectDevice.connectId); - const featuresRes = await sdk.getFeatures(selectDevice.connectId); + console.log('Loading device features for:', currentId); + const featuresRes = await sdk.getFeatures(currentId); console.log('getFeatures result:', featuresRes); - if (featuresRes.success) { + if (featuresRes.success) { const fetchedFeatures = featuresRes.payload; console.log('Features loaded successfully, now loading OneKey features...'); const fetchedOnekeyFeatures = await loadOnekeyFeatures(); - setFeatures(fetchedFeatures); - setOnekeyFeatures(fetchedOnekeyFeatures); + if (!cancelled && currentId === selectDevice?.connectId) { + setFeatures(fetchedFeatures); + setOnekeyFeatures(fetchedOnekeyFeatures); + } } else { - console.error('Failed to get features:', featuresRes.payload.error); - setError(featuresRes.payload.error); + const msg = + (featuresRes as any)?.payload?.error ?? + String((featuresRes as any)?.payload ?? 'Unknown error'); + console.error('Failed to get features:', msg); + if (!cancelled) setError(msg); } } catch (error) { console.error('Exception in loadDeviceFeatures:', error); - setError(error instanceof Error ? error.message : String(error)); + if (!cancelled) + setError(error instanceof Error ? error.message : String(error)); } finally { - setConnecting(false); + if (!cancelled) setConnecting(false); } }; loadDeviceFeatures(); + return () => { + cancelled = true; + }; }, [sdk, selectDevice?.connectId, loadOnekeyFeatures]);
669-675
: Localize messages and gate the disconnected notice by mode; add a refreshAvoid hard-coded strings. Show the disconnected tip only for desktop-web-ble. Offer a one-click refresh for OneKey Features.
- {features && !onekeyFeatures && ( - <MessageBox message="OneKey Features not available. Try clicking 'Refresh OneKey Features' button." /> - )} - {selectDevice && selectDevice.state === 'disconnected' && ( - <MessageBox message={`Device "${selectDevice.name}" shows as disconnected. In desktop-web-ble mode, this is normal - the device can still communicate via Bluetooth.`} /> - )} + {features && !onekeyFeatures && ( + <Stack padding="$2" backgroundColor="$bgCaution" alignItems="center" gap="$2"> + <Text> + {intl.formatMessage({ id: 'tip__onekey_features_unavailable' })} + </Text> + <Button size="small" onPress={loadOnekeyFeatures}> + {intl.formatMessage({ id: 'label__refresh_onekey_features' })} + </Button> + </Stack> + )} + {selectDevice && + selectDevice.state === 'disconnected' && + connectionType === 'desktop-web-ble' && ( + <MessageBox + message={intl.formatMessage( + { id: 'tip__desktop_web_ble_disconnected_notice' }, + { name: selectDevice.name } + )} + /> + )}Add the supporting imports and wiring outside this hunk:
// imports (top of file) import { connectionTypeAtom } from '../../atoms/deviceConnectAtoms'; // inside FirmwareUpdate component const connectionType = useAtomValue(connectionTypeAtom);Remember to add i18n keys:
- tip__onekey_features_unavailable
- label__refresh_onekey_features
- tip__desktop_web_ble_disconnected_notice
packages/hd-transport-electron/src/noble-ble-handler.ts (9)
1269-1285
: Unsubscribe error: normalize and keep state consistent.Don’t assume Error; also ensure state resets even if logging throws.
- notifyCharacteristic.unsubscribe((error: Error | undefined) => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }Optionally: use
toErrorMessage(error)
in a structured log field.
21-21
: Importing types from @stoprocent/noble can fail (no shipped d.ts).Prefer importing types from 'noble' or add a local shim that re-exports them. Runtime require can still use '@stoprocent/noble'.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';Alternatively, add a local declaration:
// types/@stoprocent__noble.d.ts declare module '@stoprocent/noble' { export * from 'noble'; }
598-602
: Service discovery error: normalize error type.Don’t assume Error; Noble may pass strings. Use unknown and a normalizer.
- peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error: Error | undefined, services: Service[]) => { + peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; }Also add at top:
-import { safeLog } from './types/noble-extended'; +import { safeLog, toErrorMessage } from './types/noble-extended';
616-620
: Characteristic discovery error: same normalization.- (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }
698-702
: Force reconnect error: avoid.message
on non-Error.- if (error) { + if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`)); } else {
748-751
: Fresh peripheral connect: normalize error.- if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + if (error) { + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`));
960-966
: Connection error: normalize error before constructing TypedError.- if (error) { + if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; }
1102-1106
: Chunk write error: normalize before TypedError.- writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); return; }
1201-1223
: Rebuild subscription: normalize subscribe error and avoid silent failures.- async function rebuildAppSubscription(deviceId: string, notifyCharacteristic: Characteristic): Promise<void> { + async function rebuildAppSubscription(deviceId: string, notifyCharacteristic: Characteristic): Promise<void> { notifyCharacteristic.removeAllListeners('data'); await new Promise<void>(resolve => notifyCharacteristic.unsubscribe(() => resolve())); await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) return reject(error); + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); resolve(); }); });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (7)
package.json
(2 hunks)packages/connect-examples/electron-example/package.json
(1 hunks)packages/connect-examples/electron-example/webpack.config.ts
(1 hunks)packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx
(3 hunks)packages/hd-transport-electron/package.json
(1 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(19 hunks)packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build (22)
- GitHub Check: lint (22)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
packages/connect-examples/electron-example/webpack.config.ts (1)
51-52
: Externals update verified and approvedI ran a search for any lingering
@abandonware/
references and found none. Swapping to@stoprocent/*
externals is correct and ensures native modules won’t be bundled into your main Electron build. Everything looks good—no further action needed.package.json (2)
52-52
: Handy script additionAdding dev:transport-electron improves DX for the Electron transport. No issues.
63-66
: Resolutions confirmed—no duplicate versions detectedWe ran
yarn list --pattern
for each pinned package. Each dependency appears exactly once:
- [email protected] (1 instance)
- [email protected] (1 instance)
- [email protected] (1 instance)
- [email protected] (1 instance)
All resolutions are effective and deduped. No further action needed.
packages/hd-transport-electron/src/utils/blePairing.ts (1)
43-51
: Overall design LGTM.Nice, tight probe: unsub→sub→once('data')→write, bounded by cycles and interval, with careful listener cleanup. The logs will help debug Windows “false connection” states.
Also applies to: 76-88, 92-102
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
265-266
: Runtime noble import looks good.Dynamic require of '@stoprocent/noble' at runtime is correct and pairs well with type-only imports. Nice.
92-92
: Align connection timeout with device pairing window.You probe pairing for up to ~30s elsewhere. A 15s connection timeout may abort too early on Windows. Consider 30000 ms, or make it configurable.
-const CONNECTION_TIMEOUT = 15000; // 15 seconds for device connection +const CONNECTION_TIMEOUT = 30000; // 30 seconds; align with device pairing windowLikely an incorrect or invalid review comment.
packages/connect-examples/expo-example/src/views/FirmwareScreen/index.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
packages/hd-transport-electron/src/noble-ble-handler.ts (6)
416-471
: Leak/race: remove 'discover' listener on timeout tooIf the targeted scan times out, the 'discover' listener stays attached. That can leak and fire later unexpectedly. Define the handler first so you can remove it in all paths.
- return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - if (noble) { - noble.stopScanning(); - } - logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); - resolve(null); - }, FAST_SCAN_TIMEOUT); - - // Set up discovery handler for target device - const onDiscover = (peripheral: Peripheral) => { + return new Promise((resolve, reject) => { + // Define first so we can remove in all branches + const onDiscover = (peripheral: Peripheral) => { if (peripheral.id === targetDeviceId) { - clearTimeout(timeout); + clearTimeout(timeout); if (noble) { noble.stopScanning(); } // Cache the found device discoveredDevices.set(peripheral.id, peripheral); logger?.info('[NobleBLE] Target device found:', { id: peripheral.id, name: peripheral.advertisement?.localName || 'Unknown', }); // Clean up listener if (noble) { noble.removeListener('discover', onDiscover); } resolve(peripheral); } }; + const timeout = setTimeout(() => { + if (noble) { + noble.stopScanning(); + noble.removeListener('discover', onDiscover); + } + logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); + resolve(null); + }, FAST_SCAN_TIMEOUT); + // Add discovery listener if (noble) { noble.on('discover', onDiscover); }
457-465
: Start-scanning callback: type error as unknown and normalize messageAvoid assuming Error. Use toErrorMessage for stable strings.
- noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); if (noble) { noble.removeListener('discover', onDiscover); } logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error))); return; }
899-910
: Avoid recursive reconnect wait; use bounded loopRecursive self-call can grow the stack and complicate flow. Wait in a loop, then continue.
- // Wait for ongoing operation to complete - await wait(100); - // Retry connection after waiting - return connectDevice(deviceId, webContents); + // Wait for ongoing operation to complete with a bounded loop + for (let i = 0; i < 50; i++) { // ~5s max + const s = subscriptionOperations.get(deviceId); + if (!s || s === 'idle') break; + await wait(100); + } + // Continue below
954-957
: On connection timeout, attempt to abort the in-flight connectRejecting alone can leave a dangling OS connect. Attempt a disconnect to reset state.
- const timeout = setTimeout(() => { - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, 'Connection timeout')); - }, CONNECTION_TIMEOUT); + const timeout = setTimeout(() => { + try { + connectedPeripheral.disconnect(); // best-effort abort + } catch (_) { /* ignore */ } + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, 'Connection timeout')); + }, CONNECTION_TIMEOUT);
1272-1288
: Unsubscribe callback: normalize error and keep types wideLog a clean message; don’t assume Error.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', toErrorMessage(error)); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
1295-1295
: Remove stray console.logLooks like a leftover debug marker.
- console.log('NOBLE_VERSION_771'); + // console.log removed (debug)
♻️ Duplicate comments (11)
packages/hd-transport-electron/src/noble-ble-handler.ts (11)
598-602
: Service discovery: error typing and normalizationType the error as unknown and normalize before using. Matches earlier guidance.
- peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error: Error | undefined, services: Service[]) => { + peripheral.discoverServices(ONEKEY_SERVICE_UUIDS, (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; }
616-620
: Characteristic discovery: error typing and normalizationSame fix here.
- (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }
698-702
: Normalize force reconnect errorsAvoid error.message on non-Error values.
- peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`)); } else {
748-751
: Normalize fresh connect errorsSame normalization when connecting fresh peripheral.
- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`)); } else {
960-966
: Normalize connection errorsDon’t assume Error.
- connectedPeripheral.connect((error: Error | undefined) => { + connectedPeripheral.connect((error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; }
1084-1090
: Chunk logging: don’t dump full payloadsFull hex dumps can leak data and bloat logs. Log preview and sizes only.
- logger?.info('[NobleBLE] Splitting into chunks:', { + logger?.info('[NobleBLE] Splitting into chunks:', { deviceId, totalChunks: chunks.length, chunkSize: BLE_PACKET_SIZE, totalDataLength: buffer.length, });And below (per-chunk):
- logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), - withResponse: false, + chunkPreview: `${preview}...`, + withResponse: true, });
1102-1106
: Chunk write: normalize errorsDon’t use error.message blindly.
- writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); return; }
1240-1252
: Don’t use async inside new Promise; use try/finallyThis also guarantees operation state reset on both paths.
- return new Promise(async (resolve, reject) => { - try { - await pairingProbeAndRebuild(deviceId); - subscriptionOperations.set(deviceId, 'idle'); - resolve(); - } catch (e) { - logger?.error('[NobleBLE] Pairing probe failed, forcing disconnect to reset state', { deviceId, error: e }); - // Force disconnect and cleanup on pairing failure to prevent zombie state - await disconnectDevice(deviceId); - subscriptionOperations.set(deviceId, 'idle'); - reject(e); - } - }); + try { + await pairingProbeAndRebuild(deviceId); + } catch (e) { + logger?.error('[NobleBLE] Pairing probe failed, forcing disconnect to reset state', { deviceId, error: e }); + await disconnectDevice(deviceId); + throw e; + } finally { + subscriptionOperations.set(deviceId, 'idle'); + }
1354-1355
: IPC write log: report byte count, not hex charsDivide by 2 to get bytes.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
21-21
: Fix type-only imports: prefer 'noble' types; '@stoprocent/noble' may lack d.tsKeep the runtime require from '@stoprocent/noble', but import types from 'noble' to avoid missing types at build time.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you want to keep the import from '@stoprocent/noble', add a local .d.ts shim that re-exports types from 'noble'.
1047-1065
: Single write: use with-response (safer), and normalize errorsWith-response detects link issues earlier. Also avoid calling isPairingError on unknown.
- logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + const withoutResponse = false; // safer for control packets + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); @@ - return new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + return new Promise((resolve, reject) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + if (isPairingError(error)) { reject(ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${toErrorMessage(error)}` )); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); } return; } - logger?.info('[NobleBLE] Write single ok', { deviceId }); + logger?.info('[NobleBLE] Write single ok', { deviceId });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (1)
packages/hd-transport-electron/src/noble-ble-handler.ts
(19 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: lint (22)
- GitHub Check: build (22)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
packages/hd-transport-electron/src/noble-ble-handler.ts (1)
91-91
: Revisit CONNECTION_TIMEOUT (align with Pro’s 30s auto-timeout or make it configurable)The PR text mentions a 30-second pairing/timeout window on Pro. Using 15s here may cause premature failures. Consider 30s or a config/env-based timeout.
-const CONNECTION_TIMEOUT = 15000; // 15 seconds for device connection +const CONNECTION_TIMEOUT = 30000; // 30 seconds for device connection (align with device behavior)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
packages/hd-transport/package.json (1)
10-12
: Add an npm dist-tag for the alpha.Guard against publishing this prerelease as npm “latest”.
"publishConfig": { - "access": "public" + "access": "public", + "tag": "next" },packages/hd-transport-lowlevel/package.json (1)
8-10
: Add an npm dist-tag for the alpha.Prevent accidental “latest” tagging on prereleases.
"publishConfig": { - "access": "public" + "access": "public", + "tag": "next" },packages/hd-ble-sdk/package.json (1)
13-15
: Add an npm dist-tag for the alpha.Keep prereleases off the “latest” tag.
"publishConfig": { - "access": "public" + "access": "public", + "tag": "next" },packages/core/package.json (1)
14-16
: Add an npm dist-tag for the alpha.Same rationale: avoid publishing this as “latest”.
"publishConfig": { - "access": "public" + "access": "public", + "tag": "next" },packages/connect-examples/electron-example/package.json (2)
10-21
: Rebuild native modules automatically for Electron.Ensure native deps (e.g., HCI socket) match Electron’s ABI in dev and CI.
"scripts": { "copy:inject": "node scripts/copy-injected.js", "clean:build": "rimraf out", + "postinstall": "electron-builder install-app-deps", "dev-electron-web": "cross-env NODE_ENV=development yarn copy:inject && yarn build:main && cd ../expo-example && yarn dev:electron-web",
22-30
: Pin vs caret for noble in the example.If you prefer getting patches automatically in the example app, switch to a caret. Keep it pinned if reproducibility is more important.
- "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4",
♻️ Duplicate comments (2)
packages/hd-transport-electron/package.json (1)
23-26
: Declare HCI socket as an optional peer for Linux users.Most Linux setups need @stoprocent/bluetooth-hci-socket at runtime. Add it as an optional peer to guide integrators without forcing installs on macOS/Windows.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },packages/connect-examples/electron-example/package.json (1)
23-30
: Add HCI socket to dependencies to avoid runtime module-not-found on Linux.@noble requires the HCI backend on Linux. The example app should depend on it explicitly.
"dependencies": { "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4", "electron-is-dev": "^3.0.1",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: publish-and-release
- GitHub Check: lint (22)
- GitHub Check: build (22)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (14)
packages/shared/package.json (1)
3-3
: PublishConfig tag for prerelease
- Version bump to
1.1.10-alpha.2
looks good.- Add
"tag": "next"
so npm won’t mark this prerelease aslatest
.Apply this diff in
packages/shared/package.json
:"publishConfig": { - "access": "public" + "access": "public", + "tag": "next" },Verified no lingering
"1.1.9"
pins in anypackage.json
. Ready to merge.packages/hd-transport/package.json (1)
3-3
: Version bump acknowledged.No issues spotted with the version alignment here.
packages/hd-ble-sdk/package.json (1)
3-3
: All BLE SDK dependencies are correctly pinned to 1.1.10-alpha.2I’ve checked every package.json across examples, wrappers, and SDKs. Every reference to @onekeyfe/hd-core, hd-shared, and hd-transport-react-native is locked at 1.1.10-alpha.2. No mismatches detected—no further action needed.
packages/core/package.json (1)
3-3
: Core alpha bump OK; double-check prerelease consumption plan.The deps point to the same alpha. Confirm downstreams expect an alpha and won’t auto-range to a stable ^1.1.x while missing prerelease features.
Also applies to: 28-29
packages/hd-transport-react-native/package.json (1)
3-3
: Version bump and deps alignment look good.The package version and internal deps are consistently pinned to 1.1.10-alpha.2. No concerns from this manifest.
Also applies to: 22-24
packages/hd-transport-emulator/package.json (1)
3-3
: Alpha alignment LGTM.Version and internal deps moved to 1.1.10-alpha.2. Manifest changes are clean.
Also applies to: 27-28
packages/connect-examples/expo-playground/package.json (1)
3-3
: Example app versions are in sync.Bumped to 1.1.10-alpha.2 across SDK deps. Looks consistent for the demo.
Also applies to: 20-22
packages/hd-transport-web-device/package.json (1)
3-3
: Web-device transport pinned correctly.Runtime deps and the electron devDependency are aligned to 1.1.10-alpha.2. No issues spotted.
Also applies to: 23-24, 27-27
packages/hd-web-sdk/package.json (1)
3-3
: All clear—no action neededI reran the checks:
- No mismatched @onekeyfe/hd-* versions.
- No leftover @abandonware/noble.
- All @stoprocent/noble imports live in electron code (hd-transport-electron and its example).
- No runtime dependency on @onekeyfe/hd-transport-electron outside its own package.
Everything’s tidy. Merging is safe.
packages/hd-transport-electron/package.json (1)
3-3
: Version bump looks good.Matches the repo-wide alpha bump and keeps transports aligned.
packages/hd-transport-http/package.json (1)
3-3
: Alpha version bump is consistent.No concerns.
packages/connect-examples/electron-example/package.json (1)
10-21
: Webpack externals mapping is correctI’ve confirmed that
webpack.config.ts
declares both
'@stoprocent/noble': 'commonjs @stoprocent/noble'
and
'@stoprocent/bluetooth-hci-socket': 'commonjs @stoprocent/bluetooth-hci-socket'
in theexternals
block (lines 51–52). No further changes needed.packages/connect-examples/expo-example/package.json (1)
22-25
: Version alignment across SDKs looks consistent.The Expo example tracks the alpha.2 line correctly.
packages/hd-common-connect-sdk/package.json (1)
23-28
: Good: all transport/core deps aligned to alpha.2.Keeps the SDK coherent across packages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (17)
packages/hd-transport-electron/src/noble-ble-handler.ts (17)
21-21
: Type imports may fail if @stoprocent/noble lacks TypeScript definitionsThe types
Peripheral
,Service
, andCharacteristic
are imported directly from@stoprocent/noble
. If this package doesn't ship with TypeScript definitions, the build will break.
27-27
: Fix typo in comment"bundlinpissues" should be "bundling issues".
115-120
: Reduce notification log noise to debug levelLogging every notification at info level creates excessive noise and hurts performance.
- logger?.info('[NobleBLE] Notification', { + logger?.debug('[NobleBLE] Notification', { deviceId, dataLength: data.length, });
705-708
: Normalize connect error in force reconnectThe error callback needs safe handling.
- peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));
755-758
: Fresh peripheral connection error needs normalization- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`));
970-976
: Main connection error needs normalization- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error)));
1112-1116
: Normalize chunk write error- writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error)));
1405-1405
: Log byte count, not hex string length
hexData.length
counts characters, not bytes. Divide by 2 for actual byte count.- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
68-81
: Make isPairingError accept unknown to prevent crashesThe function assumes
error
is an Error object with a.message
property. If callbacks pass strings or other types, this will crash.-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = toErrorMessage(error).toLowerCase(); return keywords.some(k => errorMessage.includes(k)); }Add this helper near the imports:
function toErrorMessage(err: unknown): string { if (typeof err === 'string') return err; if (err && typeof err === 'object' && 'message' in err) { return (err as any).message as string; } return String(err); }
601-608
: Handle non-Error callback values safelyThe service discovery callback assumes
error
is an Error. Noble callbacks can pass strings or other types.peripheral.discoverServices( ONEKEY_SERVICE_UUIDS, - (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; }
623-627
: Normalize characteristic discovery error handlingSame issue - the callback error needs normalization.
- (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }
1055-1055
: Consider using with-response writes for single packetsSingle writes currently use
withoutResponse=true
. For small control packets, with-response writes detect link issues earlier.
1058-1070
: isPairingError expects Error type but callback may pass unknownThe write callback error could be a string or other type. Normalize before calling
isPairingError
.- writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, true, (error: unknown) => { if (error) { logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + const errMsg = toErrorMessage(error); + if (isPairingError(error)) { reject( ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${errMsg}` ) ); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, errMsg)); }
1104-1111
: Don't log full chunk payloads - sensitive data riskLogging full hex data for every chunk leaks sensitive information and bloats logs.
+ const preview = chunk.toString('hex').slice(0, 32); logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), + chunkPreview: `${preview}...`, withResponse: false, });
1164-1170
: Guard against concurrent unsubscribe race conditionIf unsubscribe is in progress and subscribe is called, they'll race. Wait for idle state first.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + await wait(50); + } + }
1217-1230
: Handle unknown error types in subscribe/unsubscribe callbacksThe callbacks assume Error objects but may receive strings or other types.
async function rebuildAppSubscription( deviceId: string, notifyCharacteristic: Characteristic ): Promise<void> { notifyCharacteristic.removeAllListeners('data'); - await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) { + logger?.error('[NobleBLE] Unsubscribe before resubscribe failed:', error); + return reject(new Error(toErrorMessage(error))); + } + resolve(); + }); + }); await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { + notifyCharacteristic.subscribe((error?: unknown) => { if (error) { - reject(error); + reject(new Error(toErrorMessage(error))); return; } resolve(); }); });
1323-1323
: Normalize unsubscribe callback error- notifyCharacteristic.unsubscribe((error: Error | undefined) => { + notifyCharacteristic.unsubscribe((error: unknown) => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/hd-transport-electron/src/noble-ble-handler.ts
(20 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: Analyze (javascript-typescript)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (7)
packages/hd-transport-electron/package.json (2)
23-26
: Declare Linux HCI backend as an optional peer for clearer integrator guidance.Most Linux users will need @stoprocent/bluetooth-hci-socket at runtime. Expose it as an optional peer so consumers get a helpful install hint without breaking macOS/Windows.
Add this to package.json (no need to import it directly in code):
{ "peerDependencies": { "electron": ">=25.0.0", "electron-log": ">=4.0.0", "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, "peerDependenciesMeta": { "@stoprocent/bluetooth-hci-socket": { "optional": true } } }
28-29
: Prefer a caret range for @stoprocent/noble to receive patch fixes automatically.Pinning to 2.3.4 blocks patch updates. Consider using a caret.
- "@stoprocent/noble": "2.3.4" + "@stoprocent/noble": "^2.3.4"packages/connect-examples/electron-example/package.json (2)
23-23
: Add the Linux HCI backend explicitly to avoid “module not found” at runtime.Example apps often don’t get optional deps pulled in. Add @stoprocent/bluetooth-hci-socket to dependencies so Linux users can run out of the box.
"dependencies": { - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4",
23-23
: Add the HCI socket dependency to the Electron example for LinuxThe webpack externals are correctly pulling in all
@stoprocent/*
modules (including@stoprocent/noble
). However, the example isn’t installing or bundling the HCI socket library, which will cause BLE to fail on Linux.• In
packages/connect-examples/electron-example/package.json
, underdependencies
, add:"dependencies": { "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^<latest-version>", "debug": "4.3.4", … },
• Confirm that this new dependency is included in your production build. If you’re manually overriding
files
inelectron-builder.config.js
, make surenode_modules/@stoprocent/bluetooth-hci-socket/**
is matched (or remove restrictive globs so allnode_modules
come along).With that change, the HCI socket will be installed and bundled for Linux, and your BLE stack will work out of the box.
packages/hd-transport/package.json (1)
4-4
: Replace placeholder description for package clarity.Same feedback as earlier review: let’s swap the TODO for a concise, user-facing summary.
- "description": "> TODO: description", + "description": "Transport layer abstractions and utilities for OneKey hardware SDK.",packages/hd-transport-http/package.json (1)
27-31
: Upgrade axios (security fixes) and re-audit.axios ^0.27.2 is outdated and carries known advisories. Bump to 1.x and re-run audits.
Apply:
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.3", "@onekeyfe/hd-transport": "1.1.10-alpha.3", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Verify with:
#!/bin/bash set -e cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm ls axios || true npm audit --omit=dev || truepackages/hd-transport-lowlevel/package.json (1)
22-24
: Consider workspace protocol for internal deps.Switching to "workspace:*" reduces churn on every coordinated release. Only do this if your package manager setup supports it.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.3", - "@onekeyfe/hd-transport": "1.1.10-alpha.3" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (13)
packages/shared/package.json (1)
3-3
: Version bump looks good.No API surface changes here. This aligns with the repo-wide move to 1.1.10-alpha.3.
packages/connect-examples/expo-playground/package.json (1)
3-3
: Example app versions aligned with SDK bump.The example now targets 1.1.10-alpha.3 across core/shared/web-sdk. Good for testing the BLE migration end-to-end.
Also applies to: 20-22
packages/hd-common-connect-sdk/package.json (1)
3-3
: SDK aggregator versions synced.All internal deps moved to 1.1.10-alpha.3. No issues spotted.
Also applies to: 23-28
packages/hd-transport-react-native/package.json (1)
3-3
: Lockstep bumps confirmed; no alpha.2 remains.All
"version"
fields and internal deps now use 1.1.10-alpha.3. No stragglers found. Great job—approving.packages/hd-transport-emulator/package.json (1)
3-3
: Consistent versioning across emulator and deps.Bump matches the rest of the monorepo. No action needed.
Also applies to: 27-28
packages/hd-transport/package.json (1)
3-3
: Version bump acknowledged.No functional changes here. All good.
packages/core/package.json (1)
3-3
: Core and deps in sync.Version and internal dependency bumps are consistent with the release train.
Also applies to: 28-29
packages/hd-transport-http/package.json (1)
3-3
: Monorepo version alignment looks good.The package version matches the alpha.3 stream across the workspace.
packages/hd-transport-lowlevel/package.json (1)
3-3
: Version bump is consistent.Matches the repo-wide 1.1.10-alpha.3 line.
packages/connect-examples/expo-example/package.json (1)
3-3
: OK to bump example app version.Keeps the example aligned with the SDK release train.
packages/hd-ble-sdk/package.json (1)
3-3
: BLE SDK version bump looks good.No concerns with the alpha.3 alignment.
packages/hd-web-sdk/package.json (2)
3-3
: Version alignment acknowledged.The sdk tracks alpha.3 with the rest of the workspace.
24-27
: Lockstep internal deps make sense here.For a public web SDK, exact internal pins help avoid mismatches. No change needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/hd-transport-electron/src/noble-ble-handler.ts (1)
420-471
: Targeted scan leaks the 'discover' listener on timeout; incorrect pre-removalYou add onDiscover, but on timeout you don’t remove it. Also calling removeListener(onDiscover) before adding it does nothing. Remove any prior listeners first, and always detach on timeout.
return new Promise((resolve, reject) => { const timeout = setTimeout(() => { if (noble) { noble.stopScanning(); } logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); + // Detach the temporary listener to avoid leaks + if (noble) { + noble.removeListener('discover', onDiscover); + } resolve(null); }, FAST_SCAN_TIMEOUT); @@ - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + // Remove existing discover listeners to prevent duplication + if (noble) { + noble.removeAllListeners('discover'); + noble.on('discover', onDiscover); + } @@ - if (noble) { - noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + if (noble) { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); if (noble) { noble.removeListener('discover', onDiscover); } logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, safeToLowerMessage(error))); return; }Also applies to: 452-456
packages/hd-transport-electron/package.json (1)
16-21
: Ensure native addons are rebuilt for Electron@noble often pulls native pieces. Add an Electron rebuild step so binaries match your Electron ABI.
"scripts": { - "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", - "build": "rimraf dist && rollup -c ../../build/rollup.config.js", + "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", + "build": "rimraf dist && rollup -c ../../build/rollup.config.js", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket || true", "lint": "eslint .", "lint:fix": "eslint . --fix" },If you rely on prebuilt binaries instead, document the process and skip this script.
packages/connect-examples/electron-example/package.json (1)
32-40
: Confirm native rebuild for Electron.If
@stoprocent/bluetooth-hci-socket
compiles natively, add a postinstall step or docs to run electron-rebuild for your Electron version to prevent ABI mismatches.You can add:
"scripts": { + "postinstall": "electron-rebuild -f -w @stoprocent/bluetooth-hci-socket || true",
And ensure electron-rebuild is a devDependency.
♻️ Duplicate comments (30)
packages/hd-transport/package.json (1)
3-5
: Nice: description fixed and version aligned.This addresses the earlier “TODO description” feedback and keeps prerelease versions consistent. Nothing else to change here.
packages/hd-transport-lowlevel/package.json (1)
3-3
: Use workspace: for internal deps (same prior feedback).*Repeating earlier guidance here to reduce friction on the next version bump.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@onekeyfe/hd-transport": "1.1.10-alpha.4" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }Also applies to: 22-23
packages/hd-transport-electron/src/utils/blePairing.ts (6)
9-13
: Make write response mode configurable (don’t hardcode withoutResponse=true)Expose a
withoutResponse
option and log the chosen mode. On some stacks (especially Windows), a with-response write improves pairing reliability.export interface Step3Options { intervalMs?: number; // default 3000 maxCycles?: number; // default 10 initDataHex?: string; // hex string to write; default OneKey init + withoutResponse?: boolean; // default true } @@ - const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const withoutResponse = options.withoutResponse ?? true; @@ - writeCharacteristic.write(buffer, true, (writeError?: Error) => { - logger?.info( - `[Pairing] Listeners after attach: ${notifyCharacteristic.listenerCount('data')}` - ); + writeCharacteristic.write(buffer, withoutResponse, (writeError?: unknown) => { + logger?.info( + `[Pairing] Listeners after attach: ${notifyCharacteristic.listenerCount('data')}, withoutResponse: ${withoutResponse}` + );Also applies to: 25-27, 90-95
33-44
: Use a portable timer type and unsubscribe on cleanupNodeJS.Timeout clashes with DOM typings in Electron. Also unsubscribe the characteristic to leave a clean state and prevent lingering subscriptions.
- let intervalId: NodeJS.Timeout | null = null; + let intervalId: ReturnType<typeof setInterval> | null = null; @@ - const cleanup = () => { + const cleanup = () => { if (intervalId) clearInterval(intervalId); - // pairing阶段结束前,先移除一次性侦听器,避免泄露 + // During pairing, remove one-shot listeners to avoid leaks logger?.info( - `[Pairing] Cleaning up ${notifyCharacteristic.listenerCount('data')} listeners before cycle` + `[Pairing] Cleaning up ${notifyCharacteristic.listenerCount('data')} listeners` ); notifyCharacteristic.removeAllListeners('data'); + // Best-effort unsubscribe to avoid transient duplicate subscriptions + try { + notifyCharacteristic.unsubscribe(() => {}); + } catch { + /* noop */ + } };
58-67
: Prevent overlapping probe cycles (race/leak risk)setInterval can start a new unsubscribe→subscribe→write while the previous cycle is still running. Gate re-entry with an in-flight flag and ensure it resets on all paths. Also mark resolved before rejecting on timeout.
- const doCycle = () => { + let inFlight = false; + const doCycle = () => { - if (resolved || responseReceived) return; + if (resolved || responseReceived || inFlight) return; + inFlight = true; cycles += 1; @@ - // 取消订阅 → 重新订阅 → 写入 + // Unsubscribe → subscribe → write notifyCharacteristic.unsubscribe(() => { - notifyCharacteristic.subscribe((subscribeError?: Error) => { + notifyCharacteristic.subscribe((subscribeError?: unknown) => { if (subscribeError) { - logger?.error('[Pairing] subscribe failed', { + logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, - error: subscribeError.message, + error: toErrorMessage(subscribeError), }); - return; // 等待下一轮 + inFlight = false; + return; // wait for next round } @@ - writeCharacteristic.write(buffer, true, (writeError?: Error) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError?: unknown) => { logger?.info( `[Pairing] Listeners after attach: ${notifyCharacteristic.listenerCount('data')}` ); if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, - error: writeError.message, + error: toErrorMessage(writeError), }); - return; // 等待下一轮 + inFlight = false; + return; // wait for next round } logger?.info('[Pairing] write', { deviceId, cycle: cycles }); + inFlight = false; }); }); }); @@ - // 达到最大轮次后,停止继续调度,立即判定为超时 + // Stop scheduling when reaching max cycles and resolve/reject once if (cycles >= maxCycles && intervalId) { clearInterval(intervalId); intervalId = null; if (!resolved && !responseReceived) { const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1); logger?.info('[Pairing] timeout', { deviceId, cycles, elapsed: totalElapsed }); cleanup(); - reject(new Error('pairing_timeout')); + resolved = true; + reject(new Error('pairing_timeout')); } } }; @@ - // 立即开始第一轮,然后每 intervalMs 一轮 + // Start immediately, then repeat every intervalMs doCycle(); intervalId = setInterval(doCycle, intervalMs);Also applies to: 75-86, 90-101, 108-118, 121-124
75-85
: Normalize callback errors (avoid.message
on non-Errors)Noble callbacks can pass strings. Use
unknown
andtoErrorMessage()
.- notifyCharacteristic.subscribe((subscribeError?: Error) => { + notifyCharacteristic.subscribe((subscribeError?: unknown) => { if (subscribeError) { - logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, error: subscribeError.message }); + logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, error: toErrorMessage(subscribeError) }); return; } @@ - writeCharacteristic.write(buffer, true, (writeError?: Error) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError?: unknown) => { if (writeError) { - logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, error: writeError.message }); + logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, error: toErrorMessage(writeError) }); return; }Also applies to: 90-101
39-44
: Unify comment language and phrasingKeep comments in clear English for consistency and readability.
- // pairing阶段结束前,先移除一次性侦听器,避免泄露 + // During pairing, remove one-shot listeners to avoid leaks @@ - // 清理旧监听,确保不会累积 + // Clean previous listener to avoid accumulation @@ - // 取消订阅 → 重新订阅 → 写入 + // Unsubscribe → resubscribe → write @@ - // 仅监听一次本轮的回包 + // Listen only once for this cycle's response @@ - // 立即开始第一轮,然后每 intervalMs 一轮 + // Start immediately, then run every intervalMsAlso applies to: 68-70, 75-76, 87-89, 121-124
6-7
: Import types and error normalizer from a stable sourceImporting Characteristic types from @stoprocent/noble may fail if the fork doesn’t publish TS types. Also add a normalizer to avoid unsafe
.message
access. If you already ship a local shim, ignore the first line change; still add the normalizer import.-import type { Characteristic } from '@stoprocent/noble'; -import type { Logger } from '../types/noble-extended'; +// Prefer local shim or upstream 'noble' types to avoid missing d.ts in forks +import type { Characteristic } from 'noble'; +import type { Logger } from '../types/noble-extended'; +import { toErrorMessage } from '../types/noble-extended';packages/hd-transport-electron/src/noble-ble-handler.ts (15)
596-676
: Normalize discovery callback errors; type as unknownAvoid
.message
on non-Errors for both service and characteristic discovery.- peripheral.discoverServices( - ONEKEY_SERVICE_UUIDS, - (error: Error | undefined, services: Service[]) => { + peripheral.discoverServices( + ONEKEY_SERVICE_UUIDS, + (error: unknown, services: Service[]) => { if (error) { - logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + logger?.error('[NobleBLE] Service discovery failed:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, safeToLowerMessage(error))); return; } @@ - service.discoverCharacteristics( - [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], - (error: Error | undefined, characteristics: Characteristic[]) => { + service.discoverCharacteristics( + [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], + (error: unknown, characteristics: Characteristic[]) => { if (error) { - logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + logger?.error('[NobleBLE] Characteristic discovery failed:', error); + reject( + ERRORS.TypedError( + HardwareErrorCode.BleCharacteristicNotFound, + safeToLowerMessage(error) + ) + ); return; }
684-709
: Normalize force reconnect errorUse a safe message; don’t assume Error.
- await new Promise<void>((resolve, reject) => { - peripheral.connect((error: Error | undefined) => { + await new Promise<void>((resolve, reject) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${safeToLowerMessage(error)}`)); } else {
971-999
: Connection callback: treat error as unknown and normalizeAvoid
.message
directly; normalize before logging and rejecting.- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, safeToLowerMessage(error))); return; }
1325-1341
: Unsubscribe: callback error should be unknown; normalize messageKeep logs stable and avoid property access on non-Errors.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
21-21
: Import types from a package that guarantees .d.ts availability@stoprocent/noble may not ship types. Import TS types from 'noble' (or your local shim) while keeping runtime require to '@stoprocent/noble'.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';
27-27
: Fix typo in comment“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
67-81
: Broaden isPairingError to accept unknown; avoid unsafe .messagePrevent crashes when non-Error values flow through callbacks. Normalize once and test keywords.
-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = safeToLowerMessage(error); return keywords.some(k => errorMessage.includes(k)); } + +function safeToLowerMessage(err: unknown): string { + const msg = + typeof err === 'string' + ? err + : (err as any)?.message && typeof (err as any).message === 'string' + ? (err as any).message + : String(err); + return msg.toLowerCase(); +}
115-120
: Reduce notification log noise; prefer debug levelPer-notification info logs are noisy and can hurt perf.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, });
754-758
: Fresh peripheral connect: normalize error messageSame pattern as above.
- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${safeToLowerMessage(error)}`)); } else {
1055-1070
: Single write: prefer with-response or make it configurable; normalize errorsFor control packets, with-response (false) often detects link issues earlier. At minimum, normalize errors.
- logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + const withoutResponse = false; // safer for small control packets; consider making this configurable + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); @@ - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { - logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + logger?.error('[NobleBLE] Write single failed:', error); + if (isPairingError(error)) { reject( ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${safeToLowerMessage(error)}` ) ); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject( + ERRORS.TypedError( + HardwareErrorCode.BleWriteCharacteristicError, + safeToLowerMessage(error) + ) + ); } return; }
1094-1116
: Chunked write: don’t log full payloads; normalize write errorsLogging entire hex leaks data and is noisy. Log previews and sizes only. Also handle error as unknown.
- logger?.info('[NobleBLE] Splitting into chunks:', { + logger?.info('[NobleBLE] Splitting into chunks:', { deviceId, totalChunks: chunks.length, chunkSize: BLE_PACKET_SIZE, totalDataLength: buffer.length, }); @@ - logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), + chunkPreview: `${preview}...`, withResponse: false, }); @@ - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject( + ERRORS.TypedError( + HardwareErrorCode.BleWriteCharacteristicError, + safeToLowerMessage(error) + ) + ); return; } resolve(); });
1164-1169
: Guard subscribe against concurrent unsubscribeIf unsubscribe is in-flight, wait until idle to avoid races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { @@ return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1212-1231
: Rebuild subscription: normalize subscribe/unsubscribe callback errorsTreat callback errors as unknown; wrap with a safe message and log.
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) { + logger?.error('[NobleBLE] Unsubscribe before resubscribe failed:', error); + return reject(new Error(safeToLowerMessage(error))); + } + resolve(); + }); + }); await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { + notifyCharacteristic.subscribe((error?: unknown) => { if (error) { - reject(error); + reject(new Error(safeToLowerMessage(error))); return; } resolve(); }); });
1277-1288
: Probe write (non-Windows): normalize error callbackDon’t assume Error in the callback; log a safe message.
- await new Promise<void>((resolve, reject) => { - characteristics.write.write(probeData, false, error => { + await new Promise<void>((resolve) => { + characteristics.write.write(probeData, false, (error: unknown) => { if (error) { // We can often ignore errors here, as the goal is just to trigger the dialog. // However, log it for debugging purposes. - logger?.warn('[NobleBLE] Probe write failed (this may be expected):', error.message); + logger?.warn('[NobleBLE] Probe write failed (this may be expected):', safeToLowerMessage(error)); } resolve(); // Resolve regardless of error }); });
1407-1408
: IPC write log should report bytes, not hex characters
hexData.length
counts nibbles. Divide by 2 for bytes.- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });packages/hd-transport-web-device/package.json (1)
23-24
: Use workspace protocol for internal deps to cut churnSince this is a monorepo, prefer
workspace:^
for internal packages to keep versions in lockstep and avoid manual bumps.- "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@onekeyfe/hd-transport": "1.1.10-alpha.4" + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^" @@ - "@onekeyfe/hd-transport-electron": "1.1.10-alpha.4", + "@onekeyfe/hd-transport-electron": "workspace:^",Also applies to: 27-27
packages/hd-transport-electron/package.json (2)
23-26
: Add optional peer for Linux HCI socketGuide Linux users without forcing installs on macOS/Windows.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
28-30
: Let noble pick up patch fixes automaticallyUse a caret to receive non-breaking updates.
- "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@stoprocent/noble": "2.3.4" + "@onekeyfe/hd-shared": "1.1.10-alpha.4", + "@stoprocent/noble": "^2.3.4"packages/connect-examples/electron-example/package.json (1)
23-30
: Add @stoprocent/bluetooth-hci-socket to avoid runtime failures on Linux.Noble needs the HCI socket in Electron main on Linux. It’s not present here, so you risk "module not found".
Suggested fix:
"dependencies": { - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "2.2.3", "debug": "4.3.4",Alternatively, if you prefer to keep it optional per platform:
"dependencies": { "@stoprocent/noble": "2.3.4", "debug": "4.3.4", ... }, + "optionalDependencies": { + "@stoprocent/bluetooth-hci-socket": "2.2.3" + },Also ensure your packager doesn’t prune optional deps you actually need at runtime.
packages/hd-transport-http/package.json (1)
27-31
: Upgrade axios to a maintained 1.x release.0.27.x is old and has known advisories. Move to axios 1.x and run tests.
Proposed change:
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.4", "@onekeyfe/hd-transport": "1.1.10-alpha.4", - "axios": "^0.27.2", + "axios": "^1.6.0", "secure-json-parse": "^4.0.0" }Pick the exact 1.x you support, update types if needed, and validate any response interceptor differences.
packages/connect-examples/expo-example/package.json (1)
22-25
: Verify these exact versions exist before merging.Examples should resolve without local workspaces. Double-check that all four packages are published at these versions.
Run:
#!/bin/bash set -e for pkg in @onekeyfe/[email protected] @onekeyfe/[email protected] @onekeyfe/[email protected] @onekeyfe/[email protected]; do echo "Checking $pkg"; npm view "$pkg" version donepackages/hd-ble-sdk/package.json (1)
23-25
: Unify internal dependency strategy: prefer workspace protocol or align all to the same prerelease.You’re mixing stable (1.1.10) and alpha (.4) across tightly-coupled internal packages. This often causes subtle mismatches during local dev and publish ordering. In this monorepo, using the workspace protocol avoids that churn.
Option A (preferred): use workspaces to keep everything in lockstep.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.4" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }Option B: if you must pin, at least align to the same prerelease tag across the set.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.4" + "@onekeyfe/hd-core": "1.1.10-alpha.4", + "@onekeyfe/hd-shared": "1.1.10-alpha.4", + "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.4" }Quick check script to surface mixed stable/alpha versions across the repo:
#!/bin/bash # List internal @onekeyfe/* dependency versions and highlight mixed tags. fd package.json packages -t f -E "**/node_modules/**" \ | xargs -I{} jq -r ' .name as $pkg | (.dependencies // {}) as $deps | to_entries | map(select(.key|test("^@onekeyfe/"))) | .[] | "\($pkg)\t\(.key)\t\(.value)" ' {} \ | sort \ | awk -F'\t' '{ key=$2; ver=$3; sub(/-.*/,"",base,ver); # naive trim after first dash for grouping arr[key][ver]++ print } END { print "\n-- Summary of versions per internal dependency --" }'
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (17)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(21 hunks)packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (8)
packages/hd-transport-electron/src/utils/blePairing.ts (1)
56-57
: Validate initDataHex to avoid Buffer.from throwsGuard against invalid hex. Log clearly if parsing fails.
- const buffer = Buffer.from(initDataHex, 'hex'); + let buffer: Buffer; + try { + buffer = Buffer.from(initDataHex, 'hex'); + } catch (e) { + cleanup(); + reject(new Error(`Invalid initDataHex: ${toErrorMessage(e)}`)); + return; + }Likely an incorrect or invalid review comment.
packages/connect-examples/expo-playground/package.json (1)
3-4
: Version alignment: mixing stable and alpha can duplicate depsYou pin hd-core to 1.1.10 but use alpha.4 for hd-shared and hd-web-sdk. This can pull two trees if peer ranges don’t align. If intentional, ignore. Otherwise, align to a single train or switch internal deps to
workspace:^
.Would you like me to scan the repo for mixed OneKey versions and list any mismatches?
Also applies to: 20-23
packages/hd-common-connect-sdk/package.json (1)
23-28
: Verify package registry and version availabilityThe npm view checks against the public registry returned 404 for every package. It looks like these are hosted in a private registry. Before publishing, please:
- Ensure your .npmrc (or CI config) points to the correct registry.
- Confirm that versions 1.1.10 (hd-core) and 1.1.10-alpha.4 (all others) are actually published there.
- Double-check that mixing a stable core with alpha transports/shared APIs won’t introduce compatibility or peer-dependency issues.
packages/core/package.json (2)
3-5
: Nice: real description added.Clear and helpful.
28-36
: Do not bump axios in core aloneCore already uses axios ^0.27.2, just like HD Transport HTTP and HD Transport Emulator. Raising only core to 1.x would create drift.
• packages/core/package.json:
"axios": "^0.27.2"
• packages/hd-transport-http/package.json:"axios": "^0.27.2"
• packages/hd-transport-emulator/package.json:"axios": "^0.27.2"
If you intend to upgrade to axios 1.x, apply that change across all packages in one coordinated PR. Otherwise, leave the versions as they are.
Likely an incorrect or invalid review comment.
packages/connect-examples/expo-example/package.json (1)
3-3
: Version bump: OK.Keep the example in sync with the monorepo release train.
packages/hd-ble-sdk/package.json (1)
3-3
: Version bump looks consistent with the repo-wide prerelease.No issues spotted with the package version update to 1.1.10-alpha.4.
packages/hd-web-sdk/package.json (1)
3-3
: Version bump acknowledged.Matches the repo’s alpha.4 cadence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
.github/workflows/package-publish.yml (3)
21-36
: Set up Node in get-version job to avoid runner driftThis job runs
node -p
without installing Node. Relying on the runner’s preinstalled Node can break later. Add setup-node here for determinism.- name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Get version information id: version-info run: |
48-59
: Enable Corepack and cache Yarn to speed up CI and pin Yarn versionIf the repo uses Yarn Berry, enable Corepack and cache Yarn modules. This stabilizes the toolchain and cuts install time.
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' + cache: 'yarn' + - name: Enable Corepack (Yarn) + run: corepack enable + - name: Install dependencies run: | echo "📦 Installing dependencies..." - yarn + yarn yarn bootstrapIf you’re on Yarn Berry, prefer
yarn install --immutable
over plainyarn
to enforce lockfile fidelity.
60-67
: Preflight: fail if any package@version already exists on npmPrevent partial or duplicate publishes. Add a preflight step that checks every workspace package and exits if the version is already on npm.
- name: Build packages run: | echo "🔨 Building packages..." yarn build + + - name: Preflight check (prevent re-publish of existing versions) + env: + NPM_CONFIG_USERCONFIG: /dev/null + run: | + set -euo pipefail + echo "🔎 Checking if any package@version already exists on npm..." + node -e ' + const { readdirSync, readFileSync, statSync } = require("fs"); + const path = require("path"); + const root = path.join(process.cwd(), "packages"); + const pkgs = readdirSync(root).filter(d => statSync(path.join(root, d)).isDirectory()); + const list = pkgs.map(d => { + const p = JSON.parse(readFileSync(path.join(root, d, "package.json"), "utf8")); + return { name: p.name, version: p.version }; + }); + console.log(list); + ' > packages.json + jq -r '.[] | "\(.name)@\(.version)"' packages.json | while read line; do + if npm view "$line" version >/dev/null 2>&1; then + echo "❌ Already published: $line"; exit 1; + fi + done + echo "✅ No already-published versions found."
♻️ Duplicate comments (5)
packages/hd-common-connect-sdk/package.json (1)
3-3
: Add CHANGELOG entry and git tag for 1.1.10-alpha.4.Mirror the version bump with release notes and a tag so downstreams can trace changes.
#!/bin/bash # Look for a CHANGELOG entry and a matching tag rg -n "## \\[1\\.1\\.10-alpha\\.4\\]" -C2 --glob '!**/node_modules/**' || echo "No CHANGELOG entry found" git tag -l | rg "1\\.1\\.10-alpha\\.4" || echo "No git tag named 1.1.10-alpha.4"packages/connect-examples/expo-example/package.json (1)
22-25
: Sync example pins with actually published artifacts.Same concern as earlier review: ensure these four packages exist at 1.1.10-alpha.4 to keep the example installable.
#!/bin/bash set -euo pipefail ver="1.1.10-alpha.4" for p in "@onekeyfe/hd-ble-sdk" "@onekeyfe/hd-common-connect-sdk" "@onekeyfe/hd-core" "@onekeyfe/hd-web-sdk"; do echo -n "$p@$ver => " npm view "$p@$ver" version --silent || echo "NOT PUBLISHED" donepackages/hd-ble-sdk/package.json (1)
23-25
: Optional: adopt workspace protocol for internal deps.This eases coordinated releases across the monorepo. Only do this if your publisher resolves workspace ranges correctly.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.4", - "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.4" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }Additionally, verify this package is published before merging the example updates that consume it.
#!/bin/bash npm view "@onekeyfe/[email protected]" version --silent || echo "NOT PUBLISHED"packages/hd-web-sdk/package.json (2)
24-27
: Aligned internal deps resolve prior “stable vs alpha” skew.All @onekeyfe/hd-* deps now pin to 1.1.10-alpha.4. This addresses the earlier mismatch concern.
24-27
: Optional: switch internal deps to workspace: during active development.*Keeps local packages in lockstep and simplifies version bumps across the monorepo. If you prefer this approach, here’s the minimal diff:
- "@onekeyfe/hd-core": "1.1.10-alpha.4", - "@onekeyfe/hd-shared": "1.1.10-alpha.4", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.4", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.4" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*"Only adopt if your release process expects pinned versions to be written by a publish step.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
.github/workflows/package-publish.yml
(1 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-web-sdk/package.json
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (5)
packages/connect-examples/expo-playground/package.json (1)
3-3
: Version bump is fine for a private example.Package is marked private. No publish/release artifacts needed here. Proceed.
packages/connect-examples/expo-example/package.json (1)
3-3
: Version bump looks good.Example remains private; no release artifacts required.
packages/core/package.json (1)
4-4
: Nice—clear, user-facing description.This improves package discoverability.
packages/hd-ble-sdk/package.json (1)
3-3
: Version alignment OK.Matches the monorepo’s alpha.4 wave.
packages/hd-web-sdk/package.json (1)
3-3
: Verified: All packages updated to 1.1.10-alpha.4
- No
1.1.10-alpha.2
pins remain.- All internal
@onekeyfe/hd-*
deps use1.1.10-alpha.4
orworkspace:*
.- Connect examples in
packages/connect-examples/expo-playground
andexpo-example
reference1.1.10-alpha.4
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.husky/pre-commit (1)
11-13
: Fix NUL-delimited xargs usageYou pipe -z, but don’t pass -0 to xargs. Filenames with spaces will break.
Apply:
-git diff --name-only -z | xargs git add +git diff --name-only -z | xargs -0 git add
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
.husky/pre-commit
(1 hunks)package.json
(2 hunks)scripts/check-versions.js
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: lint (22)
🔇 Additional comments (1)
package.json (1)
33-35
: Good addition; integrates the check into publish and dev flows
- check-versions script is clear and discoverable.
- publish-packages now guards releases. Nice.
- dev:transport-electron improves DX.
No changes needed here.
Also applies to: 53-53
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/check-versions.js (1)
1-56
: Ensure recursive package discovery in check-versions.jsThe version checker currently only picks up 12 packages (maxdepth 2), but there are 15
package.json
files underpackages
(it misses the three underpackages/connect-examples
) as confirmed by your script. We need to include those nested packages in the check.● File to update:
scripts/check-versions.js
● Problematic code (shallow scan):const packageDirs = fs .readdirSync(packagesDir) .filter(file => fs.statSync(path.join(packagesDir, file)).isDirectory());● Missing entries:
– packages/connect-examples/electron-example/package.json
– packages/connect-examples/expo-example/package.json
– packages/connect-examples/expo-playground/package.jsonAction: Replace the shallow
readdirSync
+ filter logic with a recursive directory traversal (or a glob) that finds allpackage.json
underpackagesDir
(excludingnode_modules
,dist
,build
) before parsing and comparing versions.
♻️ Duplicate comments (3)
scripts/check-versions.js (3)
12-16
: Shallow scan only inspects one level; misses nested packagesThe script only reads packages/* (1-level). It skips nested workspaces like packages/connect-examples/*, giving false confidence.
Replace the one-level directory scan with a recursive finder. Add these helpers near the top (above main):
// Helpers const IGNORED_DIRS = new Set(['node_modules', '.git', 'dist', 'build']); function findPackageJsonFiles(root) { const out = []; for (const entry of fs.readdirSync(root, { withFileTypes: true })) { if (!entry.isDirectory()) continue; if (IGNORED_DIRS.has(entry.name)) continue; const dir = path.join(root, entry.name); const pkgJson = path.join(dir, 'package.json'); if (fs.existsSync(pkgJson)) out.push(pkgJson); out.push(...findPackageJsonFiles(dir)); } return out; }Then change the scan:
- const packageDirs = fs.readdirSync(packagesDir).filter(file => { - const filePath = path.join(packagesDir, file); - return fs.statSync(filePath).isDirectory(); - }); + const pkgJsonFiles = findPackageJsonFiles(packagesDir);
17-38
: Use a deterministic baseline and handle “no packages found”Using the first encountered version as baseline is non-deterministic and can change across runs. Also, when nothing is found, firstVersion stays null.
Add a baseline helper and refactor the loop:
function getBaselineVersion(list) { if (!list.length) return null; const counts = new Map(); for (const v of list) counts.set(v, (counts.get(v) || 0) + 1); let best = null, max = -1; for (const [v, c] of counts) if (c > max) { max = c; best = v; } return best; }Apply this diff to the collection loop:
- for (const pkg of packageDirs) { - const pkgJsonPath = path.join(packagesDir, pkg, 'package.json'); + for (const pkgJsonPath of pkgJsonFiles) { if (fs.existsSync(pkgJsonPath)) { try { const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')); const { name, version } = pkgJson; if (name && version) { - versions[name] = version; - if (!firstVersion) { - firstVersion = version; - } - - if (version !== firstVersion) { - hasError = true; - } + versions[name] = version; } } catch (e) { console.error(`Error parsing ${pkgJsonPath}:`, e); } } } + + const allVersions = Object.values(versions); + const baseline = getBaselineVersion(allVersions); + if (!baseline) { + console.error('❌ Error: No package.json files found under packages/.'); + process.exit(1); + } + hasError = allVersions.some(v => v !== baseline);
40-52
: Stabilize report output and messagesSort package names for stable diffs. Report against the baseline, not the first seen. Update the success message accordingly.
- if (hasError) { + if (hasError) { console.error('❌ Error: Found inconsistent package versions.'); console.log(' Please ensure all packages have the same version.'); console.log('\n--- Version Report ---'); - for (const [name, version] of Object.entries(versions)) { - const marker = version === firstVersion ? '✅' : '❌'; - console.log(` ${marker} ${name}: ${version}`); - } + const namesSorted = Object.keys(versions).sort(); + for (const name of namesSorted) { + const version = versions[name]; + const marker = version === baseline ? '✅' : '❌'; + console.log(` ${marker} ${name}: ${version}`); + } console.log('----------------------\n'); process.exit(1); } else { - console.log(`✅ All ${Object.keys(versions).length} packages are at version: ${firstVersion}`); + console.log(`✅ All ${Object.keys(versions).length} packages are at version: ${baseline}`); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/connect-examples/expo-example/webpack.config.js
(1 hunks)scripts/check-versions.js
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (1)
packages/connect-examples/expo-example/webpack.config.js (1)
15-15
: Use ‘auto’ for publicPath in Electron production buildsPlease update the production publicPath in packages/connect-examples/expo-example/webpack.config.js so that Electron builds loaded over file:// resolve assets correctly:
- // Production: assets served from root or GitHub Pages - config.output.publicPath = isElectronMode ? '/' : '/expo-example/'; + // In Electron (file://), use 'auto' to keep chunk/asset URLs relative + config.output.publicPath = isElectronMode ? 'auto' : '/expo-example/';• File: packages/connect-examples/expo-example/webpack.config.js
• Line: 15‘auto’ lets Webpack 5 compute a safe, runtime-relative path. After this change, please build the Expo example for Electron (no dev server) and confirm that all JS/CSS chunks load correctly under file://.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
packages/hd-transport-electron/src/noble-ble-handler.ts (6)
423-431
: Targeted scan: listener leak on timeout and no-op removal
- Timeout path never removes the onDiscover listener → leak.
- Removing the same onDiscover before adding it is a no-op; it won’t clear prior closures.
const timeout = setTimeout(() => { if (noble) { noble.stopScanning(); } logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); + // Ensure we remove the temporary listener to avoid leaks + try { noble?.removeListener('discover', onDiscover); } catch {} resolve(null); }, FAST_SCAN_TIMEOUT); @@ - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + // Register a temporary listener for this targeted scan + noble?.on('discover', onDiscover);Also applies to: 452-456
684-704
: Force reconnect may emit a user-visible disconnect event mid-retryYou clean state first, then call peripheral.disconnect while the existing 'disconnect' listener is still attached. That will fire handleDeviceDisconnect and send a BLE_DEVICE_DISCONNECTED event to the renderer during an internal recovery. This can confuse the UI.
-// Step 1: Clean up all device state first -cleanupDeviceState(deviceId); - -// Step 2: Force disconnect if connected +// Step 1: Remove disconnect listener to avoid spurious UI events +peripheral.removeAllListeners('disconnect'); + +// Step 2: Force disconnect if connected if (peripheral.state === 'connected') { await new Promise<void>(resolve => { peripheral.disconnect(() => { logger?.info('[NobleBLE] Force disconnect completed'); resolve(); }); }); // Wait for complete disconnection await wait(1000); } -// Step 3: Clear any remaining listeners on the peripheral +// Step 3: Clean up all device state after disconnect +cleanupDeviceState(deviceId); + +// Step 4: Clear any remaining listeners on the peripheral peripheral.removeAllListeners(); -// Step 4: Re-establish connection with longer timeout +// Step 5: Re-establish connection with longer timeout
1348-1348
: Remove stray console logconsole.log('NOBLE_VERSION_771') looks like leftover debug.
- console.log('NOBLE_VERSION_771'); + // console.log removed (debug leftover)
129-156
: Guard against pathological header lengthsOnly negative lengths are rejected. Consider a sane upper bound to prevent unbounded buffer growth on malformed data.
- // Only validate for negative lengths (which would be invalid) - if (packetState.bufferLength < 0) { + // Validate for invalid or excessive lengths + const MAX_PACKET_LENGTH = 1024 * 1024; // 1 MiB cap (tune as needed) + if (packetState.bufferLength < 0 || packetState.bufferLength > MAX_PACKET_LENGTH) { logger?.error('[NobleBLE] Invalid negative packet length detected:', { length: packetState.bufferLength, dataLength: data.length, rawHeader: data.subarray(0, Math.min(16, data.length)).toString('hex'), lengthBytes: data.subarray(5, 9).toString('hex'), }); resetPacketState(packetState); - return { isComplete: false, error: 'Invalid packet length in header' }; + return { isComplete: false, error: 'Invalid packet length in header' }; }
1047-1052
: Consider masking/shortening data previewsEven first 8 bytes or 32 nibbles can leak sensitive info. If payloads may contain secrets, gate previews behind a DEBUG flag and redact by default.
Also applies to: 1094-1100
831-836
: Retry telemetry reads cleanlyonFailedAttempt logging is helpful. Consider including the actual cause code/message in addition to error.message for faster triage.
♻️ Duplicate comments (15)
packages/hd-transport-electron/src/noble-ble-handler.ts (15)
460-473
: Start scanning callback: type error as unknown and normalizeDon’t assume Error. Preserve message safely.
- noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); - logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + logger?.error('[NobleBLE] Failed to start targeted scan:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error))); return; }
596-677
: Discovery callbacks: handle error as unknown, normalize messages, tighten char matching
- Use unknown for callback errors and normalize.
- Current 4–8 substring keying is brittle. Prefer explicit matching against known full UUIDs (lowercased, dashless).
- peripheral.discoverServices( - ONEKEY_SERVICE_UUIDS, - (error: Error | undefined, services: Service[]) => { + peripheral.discoverServices( + ONEKEY_SERVICE_UUIDS, + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; } @@ - service.discoverCharacteristics( - [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], - (error: Error | undefined, characteristics: Characteristic[]) => { + service.discoverCharacteristics( + [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; } @@ - // Find characteristics by extracting the distinguishing part of UUID + // Find characteristics by normalized UUID match (lowercased, dashless) for (const characteristic of characteristics) { const uuid = characteristic.uuid.replace(/-/g, '').toLowerCase(); - const uuidKey = uuid.length >= 8 ? uuid.substring(4, 8) : uuid; - - if (uuidKey === NORMALIZED_WRITE_UUID) { + if (uuid.endsWith(NORMALIZED_WRITE_UUID)) { writeCharacteristic = characteristic; - } else if (uuidKey === NORMALIZED_NOTIFY_UUID) { + } else if (uuid.endsWith(NORMALIZED_NOTIFY_UUID)) { notifyCharacteristic = characteristic; } }
970-999
: Connection callback: treat error as unknown and normalize; ensure disconnect path is intentional
- Use unknown for error and normalize messages.
- Confirm that calling disconnect on discovery failure should notify the renderer (disconnect listener is armed).
- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; } @@ - } catch (discoveryError) { + } catch (discoveryError) { logger?.error( '[NobleBLE] Service/characteristic discovery failed after all attempts:', discoveryError ); - // Disconnect on failure + // Disconnect on failure (this will notify renderer via disconnect listener) connectedPeripheral.disconnect(); reject(discoveryError); }
1047-1075
: Single write: withResponse vs withoutResponse; normalize errors and pairing checks
- You pass true (withoutResponse) which skips link-level ack. For control packets, with-response is safer unless you have perf constraints.
- Normalize errors and avoid unsafe isPairingError call.
- if (buffer.length <= BLE_PACKET_SIZE) { - logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + if (buffer.length <= BLE_PACKET_SIZE) { + const withoutResponse = false; // safer for control/handshake writes + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); await wait(5); return new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { - logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + logger?.error('[NobleBLE] Write single failed:', error); + const msg = toErrorMessage(error); + if (isPairingError(error)) { reject( ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${msg}` ) ); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, msg)); } return; } logger?.info('[NobleBLE] Write single ok', { deviceId });
1325-1342
: Unsubscribe callback: handle error as unknown and normalizeAvoid accessing error.message directly.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
21-21
: Import types from 'noble' (not '@stoprocent/noble') or add a local d.ts shimMany forks of noble ship without .d.ts. Importing types from '@stoprocent/noble' can break type-checking in CI. Switch type-only imports to 'noble' while keeping runtime require on '@stoprocent/noble', or add a local module declaration that re-exports types.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you prefer to stick with '@stoprocent/noble', add a shim (packages/hd-transport-electron/src/types/@stoprocent__noble.d.ts):
declare module '@stoprocent/noble' { export * from 'noble'; }
27-27
: Typo in comment“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
67-81
: Broaden isPairingError input and avoid unsafe .message accessCallbacks can pass strings or unknown. Accessing error.message can throw. Normalize first.
-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = toErrorMessage(error).toLowerCase(); return keywords.some(k => errorMessage.includes(k)); }Add helper (near imports, outside diff range):
function toErrorMessage(err: unknown): string { if (typeof err === 'string') return err; if (err && typeof err === 'object' && 'message' in (err as any) && typeof (err as any).message === 'string') { return (err as any).message as string; } try { return JSON.stringify(err); } catch { return String(err); } }
115-120
: Reduce high-frequency logs to debug levelPer-notification and per-packet-assembled info logs are noisy and can hurt perf.
- logger?.info('[NobleBLE] Notification', { + logger?.debug?.('[NobleBLE] Notification', { deviceId, dataLength: data.length, });- logger?.info('[NobleBLE] Packet assembled', { + logger?.debug?.('[NobleBLE] Packet assembled', { deviceId, totalPackets: packetState.packetCount, expectedLength: packetState.bufferLength, actualLength: packetState.buffer.length - COMMON_HEADER_SIZE, });Also applies to: 172-177
705-709
: Normalize force reconnect error messageAvoid error.message on a non-Error.
- reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));
755-758
: Normalize fresh peripheral connect errorHandle unknown and preserve message.
- if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + if (error) { + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`));
1094-1113
: Chunk write logs leak payloads; also normalize write errors and consider with-response
- Don’t log full chunk hex. Keep a short preview and sizes.
- Normalize callback error and consider with-response for reliability.
- logger?.info('[NobleBLE] Splitting into chunks:', { + logger?.info('[NobleBLE] Splitting into chunks:', { deviceId, totalChunks: chunks.length, chunkSize: BLE_PACKET_SIZE, totalDataLength: buffer.length, }); @@ - const writeChunk = (chunk: Buffer, chunkIndex: number): Promise<void> => + const writeChunk = (chunk: Buffer, chunkIndex: number): Promise<void> => new Promise<void>((resolve, reject) => { - logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), - withResponse: false, + chunkPreview: `${preview}...`, + withResponse: false, }); - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); return; } resolve(); }); });
1164-1169
: Guard subscribe against in-flight unsubscribeIf state is 'unsubscribing', wait until idle before proceeding to avoid races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait for unsubscribe to settle + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1212-1230
: Rebuild subscription: normalize subscribe/unsubscribe errors; avoid async Promise executor
- Use unknown for errors and normalize.
- Avoid async executor anti-pattern; use straightforward async/await.
- async function rebuildAppSubscription( + async function rebuildAppSubscription( deviceId: string, notifyCharacteristic: Characteristic ): Promise<void> { notifyCharacteristic.removeAllListeners('data'); - await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) { + logger?.error('[NobleBLE] Unsubscribe before resubscribe failed:', error); + return reject(new Error(toErrorMessage(error))); + } + resolve(); + }) + ); + await new Promise<void>((resolve, reject) => + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }) + );
1407-1407
: IPC write log reports hex chars, not bytesLog byte length for clarity.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/hd-transport-electron/src/noble-ble-handler.ts
(21 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (4)
packages/hd-transport-electron/src/noble-ble-handler.ts (4)
25-25
: LGTM: pairing probe integration is clear and scopedImporting and using runPairingProbe aligns with the PR goal and keeps platform-specific logic contained.
945-947
: LGTM: short settle delay after reusing an existing connectionThe 300ms wait helps stabilize the link before discovery. Reasonable trade-off.
1361-1366
: Nice: safeLog used for IPC pathsGood use of safeLog to avoid logger null checks and keep structured logs.
1027-1045
: Error path on hex conversion is solidYou catch Buffer.from errors and wrap them in a TypedError. Good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/hd-transport-electron/src/noble-ble-handler.ts (1)
423-429
: Clean up discover listener on timeout; remove no-op pre-remove.You add the listener but don’t remove it on timeout, which leaks. Also, calling removeListener(onDiscover) before adding does nothing.
- const timeout = setTimeout(() => { + const timeout = setTimeout(() => { if (noble) { noble.stopScanning(); + // Remove the temporary discover handler to avoid leaks + noble.removeListener('discover', onDiscover); } logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); resolve(null); }, FAST_SCAN_TIMEOUT); @@ - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + if (noble) noble.on('discover', onDiscover);Also applies to: 452-456
♻️ Duplicate comments (17)
packages/connect-examples/expo-example/package.json (1)
3-3
: Verify alpha.4 is actually published before merging.The example pins four @onekeyfe packages to 1.1.10-alpha.4. Sanity-check these exist in the registry to keep the example installable.
#!/bin/bash # Verify the exact versions exist in the npm registry set -euo pipefail for pkg in @onekeyfe/hd-ble-sdk @onekeyfe/hd-common-connect-sdk @onekeyfe/hd-core @onekeyfe/hd-web-sdk; do echo "Checking $pkg@1.1.10-alpha.4" npm view "$pkg@1.1.10-alpha.4" version doneAlso applies to: 22-25
packages/hd-transport-electron/src/noble-ble-handler.ts (16)
596-676
: Normalize discovery callback errors (service/characteristic) to avoid crashes.Don’t assume Error; log and reject with safe strings.
- peripheral.discoverServices( + peripheral.discoverServices( ONEKEY_SERVICE_UUIDS, - (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; } @@ - service.discoverCharacteristics( + service.discoverCharacteristics( [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }
1324-1341
: Unsubscribe: normalize error typing and message.Treat callback error as unknown and keep state machine consistent.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); } @@ // 🔒 Clear operation state subscriptionOperations.set(deviceId, 'idle'); resolve(); }); });
21-21
: Type imports from @stoprocent/noble may not exist; import types from 'noble' instead.Many forks don't ship d.ts. Keep the runtime require on @stoprocent/noble but import types from 'noble' (or add a local shim).
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you prefer to keep @stoprocent/noble for types, add a local module declaration that re-exports types from 'noble'.
27-27
: Typo in comment.“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
67-81
: Broaden pairing error detection and avoid unsafe .message access.Handle unknown/string errors and centralize normalization.
-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = toErrorMessage(error).toLowerCase(); return keywords.some(k => errorMessage.includes(k)); }Add this helper near the imports:
function toErrorMessage(err: unknown): string { if (typeof err === 'string') return err; if (err && typeof err === 'object' && 'message' in (err as any) && typeof (err as any).message === 'string') { return (err as any).message as string; } try { return JSON.stringify(err); } catch { return String(err); } }
115-120
: Reduce notification log noise; use debug level.Per-packet info logs are expensive and chatty.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, });
704-709
: Force reconnect: don’t use error.message directly.Coerce unknown to string.
- peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));
755-758
: Fresh peripheral connect: normalize error before using it.- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`));
971-976
: Initial connect: treat callback error as unknown and normalize.- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; }
1053-1061
: Single write: consider with-response for control packets; fix logging.With-response detects link issues sooner. If you keep withoutResponse, reflect it in logs.
- if (buffer.length <= BLE_PACKET_SIZE) { - logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + if (buffer.length <= BLE_PACKET_SIZE) { + const withoutResponse = false; // safer for small control packets; set true if you prefer throughput + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); await wait(5); return new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + if (isPairingError(error)) { reject( ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${toErrorMessage(error)}` ) ); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); } return; } logger?.info('[NobleBLE] Write single ok', { deviceId });
1104-1113
: Don’t log full chunk payloads; trim to a short preview.Prevents leaking data and noisy logs.
- logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), - withResponse: false, + chunkPreview: `${preview}...`, + withoutResponse: true, }); @@ - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); return; } resolve(); });
1164-1169
: Guard subscribe against in-flight unsubscribe to avoid races.Wait until state returns to idle before proceeding.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1212-1230
: Rebuild subscription: normalize unsubscribe/subscribe callback errors.Avoid assuming Error.
async function rebuildAppSubscription( deviceId: string, notifyCharacteristic: Characteristic ): Promise<void> { notifyCharacteristic.removeAllListeners('data'); - await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) { + logger?.error('[NobleBLE] Unsubscribe before resubscribe failed:', error); + return reject(new Error(toErrorMessage(error))); + } + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + });
1277-1290
: Non-Windows probe: comment contradicts code; fix arg and normalize error.If “no response is expected,” pass withoutResponse=true and log safely.
- // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. - // This is a dummy write operation that doesn't expect a response. + // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. + // This is a dummy write operation; no response is expected. logger?.info(`[NobleBLE] Performing probe write to trigger pairing on ${process.platform}`); const probeData = Buffer.from('01', 'hex'); // A single byte is sufficient await new Promise<void>((resolve, reject) => { - characteristics.write.write(probeData, false, error => { + characteristics.write.write(probeData, true, (error: unknown) => { if (error) { // We can often ignore errors here, as the goal is just to trigger the dialog. // However, log it for debugging purposes. - logger?.error('[NobleBLE] Probe write failed (this may be expected):', error.message); + logger?.debug('[NobleBLE] Probe write failed (this may be expected):', toErrorMessage(error)); } resolve(); // Resolve regardless of error }); });
1407-1408
: IPC write log: report bytes, not hex string length.hexData.length counts nibbles.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
97-98
: Connection timeout likely mismatches the 30s Windows pairing window; make it configurable.Expose via env with a sane default (30s). If you truly want 3s (per discussion), set the env in your build/run.
-const CONNECTION_TIMEOUT = 15000; // 15 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 30000);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
packages/connect-examples/electron-example/electron-builder.config.js
(1 hunks)packages/connect-examples/electron-example/webpack.config.ts
(1 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(21 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (3)
packages/connect-examples/electron-example/electron-builder.config.js (1)
14-18
: Good call unpacking native BLE modules; add a note to keep these in sync with externals.asarUnpack entries look correct for dynamically loaded .node binaries. Ensure these stay aligned with webpack externals and actual runtime deps to avoid loading failures at startup.
If you want, I can add a quick checklist to CI to assert those paths exist post-install on all OS targets.
packages/connect-examples/electron-example/webpack.config.ts (1)
51-53
: Externals update looks right; abandonware entries are gone.Nice cleanup. This matches the migration to @stoprocent/* and avoids bundling native layers.
packages/connect-examples/expo-example/package.json (1)
10-10
: Nice: add memory headroom for electron-web build.The 4GB heap avoids OOMs in larger trees.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
1325-1341
: Unsubscribe: normalize error typeDo not assume Error in callback.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
1348-1354
: Leftover debug logDrop the bare console.log('NOBLE_VERSION_771') or tie it to a debug flag.
- console.log('NOBLE_VERSION_771'); + // console.log('NOBLE_VERSION_771'); // enable for local diagnostics only
♻️ Duplicate comments (23)
packages/connect-examples/electron-example/package.json (1)
23-31
: Linux runtime risk: add @stoprocent/bluetooth-hci-socket explicitlyOn many Linux setups noble requires the HCI socket native addon. It often won’t arrive via optional deps in app packaging. Add it explicitly to avoid “module not found” at runtime.
"dependencies": { "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4",
Also ensure electron-builder asarUnpack includes both noble and the HCI socket folders (looks handled elsewhere—please re-verify in this example app).
packages/hd-transport-electron/package.json (3)
24-26
: Guide Linux integrators: declare HCI socket as optional peerMake the expected native piece visible without forcing it on macOS/Windows. Add as an optional peer.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Native addon ABI: plan for Electron rebuildsnoble/HCI socket are native. Ensure rebuild against your Electron ABI. Consider providing a postinstall or docs in this package (or in the example app) to run electron-rebuild.
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket || echo \"skip electron-rebuild\"" },If you prefer not to run rebuilds from a library, ship a clear README note and a check script in the example app instead.
28-32
: Allow patch updates for noblePinning blocks security/bugfix patches. Prefer a caret.
- "@stoprocent/noble": "2.3.4" + "@stoprocent/noble": "^2.3.4"packages/hd-transport-electron/src/utils/blePairing.ts (4)
11-15
: Make write-without-response configurable and log itHardcoding withoutResponse can hurt pairing on Windows. Expose it as an option and log the mode.
export interface Step3Options { intervalMs?: number; // default 3000 maxCycles?: number; // default 10 initDataHex?: string; // hex string to write; default OneKey init + withoutResponse?: boolean; // default true } @@ - const maxCycles = options.maxCycles ?? 10; - const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const maxCycles = options.maxCycles ?? 10; + const initDataHex = options.initDataHex ?? getDefaultInitHex(); + const withoutResponse = options.withoutResponse ?? true; @@ - writeCharacteristic.write(buffer, true, (writeError?: Error) => { - logger?.info( - `[Pairing] Listeners after attach: ${notifyCharacteristic.listenerCount('data')}` - ); + writeCharacteristic.write(buffer, withoutResponse, (writeError?: Error) => { + logger?.info( + `[Pairing] Listeners after attach: ${notifyCharacteristic.listenerCount('data')}, withoutResponse: ${withoutResponse}` + );Also applies to: 33-35, 97-101
40-41
: Use portable timer type (Electron typings)NodeJS.Timeout clashes with DOM typings. Use ReturnType.
- let intervalId: NodeJS.Timeout | null = null; + let intervalId: ReturnType<typeof setInterval> | null = null;
44-51
: Clean up subscription too (avoid residual subscriptions)Remove 'data' listeners and unsubscribe to leave a clean state after the probe.
const cleanup = () => { if (intervalId) clearInterval(intervalId); - // pairing阶段结束前,先移除一次性侦听器,避免泄露 - logger?.info( - `[Pairing] Cleaning up ${notifyCharacteristic.listenerCount('data')} listeners before cycle` - ); + // Remove one-shot listeners before leaving pairing to avoid leaks + logger?.info( + `[Pairing] Cleaning up ${notifyCharacteristic.listenerCount('data')} listeners before cycle` + ); notifyCharacteristic.removeAllListeners('data'); + try { + // Best-effort unsubscribe; ignore errors + // @ts-ignore subscribe API guarantees callback form + notifyCharacteristic.unsubscribe(() => {}); + } catch (_) { /* noop */ } };
46-47
: Unify comment language and phrasingUse concise English for consistency and readability.
- // pairing阶段结束前,先移除一次性侦听器,避免泄露 + // Remove one-shot listeners before finishing pairing to avoid leaks @@ - // 清理旧监听,确保不会累积 + // Clean previous listeners to avoid accumulation @@ - // 立即开始第一轮,然后每 intervalMs 一轮 + // Start immediately, then repeat every intervalMsAlso applies to: 75-80, 128-131
packages/hd-transport-electron/src/noble-ble-handler.ts (15)
970-999
: Connect callback: normalize error and avoid 3s timeout flapsUse the configurable timeout (see earlier) and normalize error.
- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { - logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + logger?.error('[NobleBLE] Connection failed:', error); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, msg)); return; }
21-21
: Type imports from @stoprocent/noble may failSome noble forks ship no types. Import types from 'noble' or add a local d.ts shim that re-exports them. Keep runtime require('@stoprocent/noble').
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you prefer keeping @stoprocent/noble for types, declare a module shim that re-exports 'noble' types.
97-98
: 3s connect timeout is too short; align with pairing windowWindows pairing can run up to 30s. A 3s timeout will flap. Make it configurable and default to 30s.
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 30000);
115-119
: Turn chatty logs to debugPer-notification and per-packet logs at info are noisy. Use debug to reduce log volume and CPU.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { @@ - logger?.info('[NobleBLE] Packet assembled', { + safeLog(logger, 'debug', '[NobleBLE] Packet assembled', {Also applies to: 172-177
601-676
: Discovery callbacks: treat errors as unknown and normalizeAvoid
.message
on strings. Normalize before logging and rejecting.- peripheral.discoverServices( + peripheral.discoverServices( ONEKEY_SERVICE_UUIDS, - (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { - logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + logger?.error('[NobleBLE] Service discovery failed:', error); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, msg)); return; } @@ - service.discoverCharacteristics( + service.discoverCharacteristics( [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { - logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + logger?.error('[NobleBLE] Characteristic discovery failed:', error); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, msg)); return; }If you already have a toErrorMessage helper, prefer importing and using it.
704-709
: Normalize error in force reconnectAvoid assuming Error.
- logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + logger?.error('[NobleBLE] Force reconnect failed:', error); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + reject(new Error(`Force reconnect failed: ${msg}`));
754-758
: Normalize error in fresh peripheral connectSame here.
- if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + if (error) { + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + reject(new Error(`Fresh peripheral connection failed: ${msg}`));
1055-1075
: Single write: prefer with-response; normalize error; surface pairing cleanlyWith-response (false) detects link issues early. If you keep withoutResponse, add a comment why.
- logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + const withoutResponse = false; // safer for small control/control-like packets + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); @@ - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { - logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + logger?.error('[NobleBLE] Write single failed:', error); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + if (isPairingError(error as any)) { reject( ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${msg}` ) ); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, msg)); } return; }
1094-1113
: Chunk write: do not log full payload; normalize errorsTrim logs and avoid leaking payloads.
- logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), + chunkPreview: `${preview}...`, withResponse: false, }); @@ - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { - logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, msg)); return; }Also applies to: 1112-1116
1164-1169
: Guard subscribe against in‑flight unsubscribeIf an unsubscribe is running, wait until idle to avoid races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { @@ return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1212-1230
: Normalize subscribe/unsubscribe callback errorsTreat callback arg as unknown and coerce to Error string safely.
async function rebuildAppSubscription( deviceId: string, notifyCharacteristic: Characteristic ): Promise<void> { notifyCharacteristic.removeAllListeners('data'); - await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) { + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + return reject(new Error(msg)); + } + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) { + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + return reject(new Error(msg)); + } + resolve(); + }); + });
1277-1293
: Probe write (non‑Windows): comment contradicts code; normalize errorThe comment says “doesn't expect a response” but you pass false (with response). Either pass true or fix the comment. Also handle unknown error.
- // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. - // This is a dummy write operation that doesn't expect a response. + // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. + // This is a dummy write operation; no response is expected. logger?.info(`[NobleBLE] Performing probe write to trigger pairing on ${process.platform}`); const probeData = Buffer.from('01', 'hex'); // A single byte is sufficient await new Promise<void>((resolve, reject) => { - characteristics.write.write(probeData, false, error => { - if (error) { + characteristics.write.write(probeData, true, (error: unknown) => { + if (error) { // We can often ignore errors here, as the goal is just to trigger the dialog. // However, log it for debugging purposes. - logger?.error('[NobleBLE] Probe write failed (this may be expected):', error.message); + const msg = (typeof error === 'object' && error && 'message' in (error as any)) + ? (error as any).message + : String(error); + logger?.debug('[NobleBLE] Probe write failed (this may be expected):', msg); } resolve(); // Resolve regardless of error }); });
1407-1408
: IPC write log: report bytes, not hex charshexData.length counts hex chars. Divide by 2 for bytes.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
67-81
: Make isPairingError safer and reusableAccept unknown and normalize once. Avoid
.message
on strings. If you have a shared toErrorMessage helper, reuse it.-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = (typeof error === 'object' && error && 'message' in (error as any)) + ? String((error as any).message).toLowerCase() + : String(error).toLowerCase(); return keywords.some(k => errorMessage.includes(k)); }If you prefer, add a small
toErrorMessage(err: unknown): string
helper near the imports and use it across this file.
27-27
: Typo in comment“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(21 hunks)packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)packages/hd-transport/src/index.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: build (22)
- GitHub Check: lint (22)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/hd-transport/src/index.ts (1)
13-15
: Re‑exporting protobuf may re‑run protobuf configuration; ensure idempotencyThis index already sets
protobuf.util.Long = Long
and callsprotobuf.configure()
(Lines 16–17). Re‑exporting./serialization/protobuf
may import a module that does the same, leading to duplicate configuration and subtle runtime issues. Guard the configuration inserialization/protobuf
so it only runs once, or refactor that module to export helpers without side effects.Suggested guard inside
serialization/protobuf.ts
:let configured = false; export function configureProtobufOnce(pb: typeof import('protobufjs/light'), LongImpl: typeof import('long')) { if (configured) return; pb.util.Long = LongImpl as any; pb.configure(); configured = true; }Then ensure either:
- Only this index configures protobuf (keep Lines 16–17) and
serialization/protobuf
exports types/utilities without side effects; or- Remove Lines 16–17 here and instead call
configureProtobufOnce
from a single, well-known entry point.Also, widening the public API surface via these exports may warrant at least a minor bump when leaving prerelease.
To verify duplication quickly:
#!/bin/bash # Look for protobuf.configure() or Long assignment inside serialization modules fd -t f 'protobuf.ts' packages | xargs rg -nP -C2 'protobuf\.(util\.Long|configure\s*\()' # Look for existing idempotent flags rg -nP '__hdTransportConfigured|configure(Protobuf)?Once|configured\s*=' packages/hd-transport/src/serialization
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/connect-examples/electron-example/src/index.ts
(1 hunks)packages/hd-transport/src/index.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
packages/connect-examples/electron-example/src/index.ts (1)
211-212
: Removing BLE init from createMainWindow prevents double‑init on reloads.Good move. This aligns with the earlier recommendation to avoid per‑reload init and native load crashes.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/connect-examples/electron-example/src/index.ts
(2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: build
- GitHub Check: Analyze (actions)
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (2)
packages/connect-examples/electron-example/src/index.ts (2)
287-294
: Solid: try/catch + structured logs around Noble init.Nice defensive wrapper and clear logs.
287-294
: Packaging already unpacks native modulesI checked
packages/connect-examples/electron-example/electron-builder.config.js
and confirmed it includes:• asarUnpack:
node_modules/@stoprocent/noble/**
node_modules/@stoprocent/bluetooth-hci-socket/**
This satisfies the requirement to unpack those native binaries at runtime. No further changes are needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/hd-transport-electron/src/noble-ble-handler.ts (3)
420-456
: Targeted scan listener management is wrong; leaks possible.You call removeListener('discover', onDiscover) before adding it, and you never remove the listener on timeout. Clean up in all paths.
- const onDiscover = (peripheral: Peripheral) => { + const onDiscover = (peripheral: Peripheral) => { if (peripheral.id === targetDeviceId) { - clearTimeout(timeout); - if (noble) { - noble.stopScanning(); - noble.removeListener('discover', onDiscover); - } + cleanup(); // Cache the found device discoveredDevices.set(peripheral.id, peripheral); logger?.info('[NobleBLE] OneKey device found during targeted scan:', { id: peripheral.id, name: peripheral.advertisement?.localName || 'Unknown', }); resolve(peripheral); } }; - - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + const cleanup = () => { + clearTimeout(timeout); + if (noble) { + noble.removeListener('discover', onDiscover); + noble.stopScanning(); + } + }; + if (noble) noble.on('discover', onDiscover); @@ - const timeout = setTimeout(() => { - if (noble) { - noble.stopScanning(); - } - logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); - resolve(null); - }, FAST_SCAN_TIMEOUT); + const timeout = setTimeout(() => { + logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); + cleanup(); + resolve(null); + }, FAST_SCAN_TIMEOUT); @@ - if (error) { - clearTimeout(timeout); - if (noble) { - noble.removeListener('discover', onDiscover); - } + if (error) { + cleanup(); logger?.error('[NobleBLE] Failed to start targeted scan:', error); reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); return; }
744-763
: Fresh-scan connect path misses a disconnect listener.You set connectedDevices for freshPeripheral but never call setupDisconnectListener, so disconnects won’t be handled.
Option A (preferred): pass webContents into connectAndDiscoverWithFreshScan and set the listener after connecting.
-async function connectAndDiscoverWithFreshScan(deviceId: string): Promise<CharacteristicPair> { +async function connectAndDiscoverWithFreshScan( + deviceId: string, + webContents: WebContents +): Promise<CharacteristicPair> { @@ - freshPeripheral.connect((error: unknown) => { + freshPeripheral.connect((error: unknown) => { if (error) { reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`)); } else { connectedDevices.set(deviceId, freshPeripheral); + setupDisconnectListener(freshPeripheral, deviceId, webContents); resolve(); } });And update callers:
-const characteristics = await connectAndDiscoverWithFreshScan(deviceId); +const characteristics = await connectAndDiscoverWithFreshScan(deviceId, webContents);Also applies to: 980-984
905-917
: Avoid recursion while waiting for subscription operations.Repeated recursion risks stack growth. Poll in a loop instead.
- await wait(100); - // Retry connection after waiting - return connectDevice(deviceId, webContents); + while (subscriptionOperations.get(deviceId) !== 'idle') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + }
♻️ Duplicate comments (19)
packages/hd-transport-electron/package.json (3)
23-26
: Add optional peer for Linux HCI backend.Declare @stoprocent/bluetooth-hci-socket as an optional peer to guide Linux users without forcing it on macOS/Windows.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Ensure native addons rebuild for Electron.@noble’s stack can include native modules; rebuild against Electron’s ABI to avoid runtime load errors. Add electron-rebuild and a postinstall hook.
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket || true" }, @@ "devDependencies": { "@types/web-bluetooth": "^0.0.17", "electron": "^25.0.0", + "electron-rebuild": "^3.6.0", "typescript": "^5.3.3" }Also applies to: 34-38
31-31
: Relax noble to caret to pick up patch fixes.Allow non-breaking updates automatically.
- "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4",packages/hd-transport-electron/src/noble-ble-handler.ts (16)
684-706
: Force reconnect: normalize errors on connect failure.Avoid .message on non-Error; surface clear messages.
- await new Promise<void>((resolve, reject) => { - peripheral.connect((error: Error | undefined) => { + await new Promise<void>((resolve, reject) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`)); } else {Also applies to: 708-708
753-763
: Fresh scan connect: normalize error type and message.Same issue here.
- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`)); } else {
970-999
: Connect callback: normalize error type and message.Treat error as unknown and avoid property access on non-Errors.
- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; } @@ - const characteristics = await connectAndDiscoverWithFreshScan(deviceId); + const characteristics = await connectAndDiscoverWithFreshScan(deviceId, webContents);
1325-1341
: Unsubscribe: treat callback error as unknown.Normalize the message; avoid property access on strings.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
21-21
: Import types from 'noble' or add a shim; @stoprocent/noble may lack d.ts.Avoid broken type resolution when the fork ships runtime only.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you prefer sticking to @stoprocent/noble everywhere, add a local .d.ts that re-exports types from 'noble'.
27-27
: Fix typo in comment.“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
67-81
: Make isPairingError robust to non-Error throws; add toErrorMessage helper.Callbacks sometimes pass strings. Accessing .message will crash.
-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { - const keywords = [ + const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = toErrorMessage(error).toLowerCase(); return keywords.some(k => errorMessage.includes(k)); }Add this helper near the imports:
function toErrorMessage(err: unknown): string { if (typeof err === 'string') return err; if (err && typeof err === 'object' && 'message' in (err as any) && typeof (err as any).message === 'string') { return (err as any).message as string; } try { return JSON.stringify(err); } catch { return String(err); } }
97-97
: 3s connect timeout is too aggressive; align with 30s probe or make configurable.Windows pairing probe runs up to 30s. A 3s connect timeout will fire early.
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 30000); // 30s default; override via env
115-120
: Reduce log noise on hot paths (notifications/assembly).Use debug for per-packet logs. Keep info for summaries/errors.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, }); @@ - logger?.info('[NobleBLE] Initialized new packet state for device:', deviceId); + safeLog(logger, 'debug', 'Initialized new packet state', { deviceId }); @@ - logger?.info('[NobleBLE] Packet assembled', { + safeLog(logger, 'debug', 'Packet assembled', { deviceId, totalPackets: packetState.packetCount, expectedLength: packetState.bufferLength, actualLength: packetState.buffer.length - COMMON_HEADER_SIZE, });Also applies to: 126-127, 172-178
601-676
: Normalize discovery callback errors; don’t assume Error.Type the callback error as unknown and use toErrorMessage for logging and TypedError.
- (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { - logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + logger?.error('[NobleBLE] Service discovery failed:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; } @@ - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }
1055-1070
: Single write: use with-response for reliability and normalize errors.With-response detects link issues earlier; also guard error typing.
- logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + const withoutResponse = false; // safer for control packets + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); @@ - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { - logger?.error('[NobleBLE] Write single failed:', error); - if (isPairingError(error)) { + logger?.error('[NobleBLE] Write single failed:', error); + if (isPairingError(error)) { reject( ERRORS.TypedError( HardwareErrorCode.BleWriteCharacteristicError, - `Pairing required for write operation: ${error.message}` + `Pairing required for write operation: ${toErrorMessage(error)}` ) ); } else { - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); } return; } - logger?.info('[NobleBLE] Write single ok', { deviceId }); + logger?.info('[NobleBLE] Write single ok', { deviceId });
1094-1113
: Chunked writes: redact payloads, use with-response toggle, normalize errors.Do not log full data. Normalize error type.
- logger?.info('[NobleBLE] Splitting into chunks:', { + logger?.info('[NobleBLE] Splitting into chunks:', { deviceId, totalChunks: chunks.length, chunkSize: BLE_PACKET_SIZE, totalDataLength: buffer.length, }); @@ - logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), - withResponse: false, + chunkPreview: `${preview}...`, + withoutResponse: true, }); @@ - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); return; } resolve(); });If you want higher reliability over throughput, set withoutResponse to false and keep a slightly larger CHUNK_WRITE_DELAY.
Also applies to: 1112-1116
1164-1169
: Guard against concurrent ‘unsubscribing’ state before subscribing.Avoid race with in-flight unsubscribe.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1212-1231
: Subscribe/unsubscribe: treat callback errors as unknown and normalize.Prevent crashes on string errors and keep logs consistent.
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) { + logger?.error('[NobleBLE] Unsubscribe before resubscribe failed:', error); + return reject(new Error(toErrorMessage(error))); + } + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + }); @@ - notifyCharacteristic.on('data', (data: Buffer) => { + notifyCharacteristic.on('data', (data: Buffer) => { const result = processNotificationData(deviceId, data); if (result.error) { logger?.error('[NobleBLE] Packet processing error:', result.error); return; } if (result.isComplete && result.completePacket) { const appCb = notificationCallbacks.get(deviceId); if (appCb) appCb(result.completePacket); } });Also applies to: 1232-1243
1277-1293
: Non-Windows probe write: comment contradicted behavior; fix and normalize error.Pass withoutResponse=true if you don’t expect a response; also handle unknown error type.
- // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. - // This is a dummy write operation that doesn't expect a response. + // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. + // This is a dummy write operation; no response is expected. logger?.info(`[NobleBLE] Performing probe write to trigger pairing on ${process.platform}`); const probeData = Buffer.from('01', 'hex'); // A single byte is sufficient await new Promise<void>((resolve, reject) => { - characteristics.write.write(probeData, false, error => { + characteristics.write.write(probeData, true, (error: unknown) => { if (error) { // We can often ignore errors here, as the goal is just to trigger the dialog. // However, log it for debugging purposes. - logger?.error('[NobleBLE] Probe write failed (this may be expected):', error.message); + logger?.debug('[NobleBLE] Probe write failed (this may be expected):', toErrorMessage(error)); } resolve(); // Resolve regardless of error }); });
1414-1415
: Log byte length, not hex string length.hexData.length counts nibbles.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (2)
packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(22 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (2)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
25-25
: LGTM: pairing probe import.Importing runPairingProbe here keeps pairing logic modular. No issues.
271-271
: Runtime require of @stoprocent/noble: OK, but ensure dependency and guard bundlers.Good for Electron main to avoid bundling issues. Keep it in dependencies (done) and document that bundlers should not try to tree-shake it out.
If you see packager issues, consider:
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require noble = require('@stoprocent/noble') as NobleModule; // Optionally assert presence: if (!noble) throw ERRORS.TypedError(HardwareErrorCode.RuntimeError, 'Failed to load noble runtime');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/build-example-desktop.yml (1)
40-46
: Fail fast if artifacts are missingCatch packaging issues early by failing when no .exe is produced.
- name: Upload Windows Artifacts uses: actions/upload-artifact@v4 with: name: win-x64-example-desktop path: | ${{ env.PROJECT_PATH }}/out/*.exe + if-no-files-found: error
♻️ Duplicate comments (1)
packages/connect-examples/electron-example/package.json (1)
24-32
: Add the HCI socket dependency explicitly to avoid runtime errors (Linux)Noble needs
@stoprocent/bluetooth-hci-socket
at runtime on Linux. It isn’t guaranteed to install transitively. Add it here to prevent “module not found”."dependencies": { - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "2.2.3", "debug": "4.3.4", @@ - "node-fetch": "^2.6.7", + "node-fetch": "^2.6.7", "@onekeyfe/hd-transport-electron": "1.1.10-alpha.4" },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
.github/workflows/build-example-desktop.yml
(1 hunks)package.json
(2 hunks)packages/connect-examples/electron-example/package.json
(2 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeQL
.github/workflows/build-example-desktop.yml
[warning] 7-45: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (4)
package.json (2)
33-35
: Version guard before publish looks goodNice addition. This prevents mismatched versions from slipping into a publish.
54-55
: Dev script addition is fineThe new
dev:transport-electron
script aligns with changes in hd-transport-electron.packages/connect-examples/electron-example/package.json (2)
30-31
: Double-check node-fetch necessityElectron 28 bundles a Node runtime with
fetch
available in many contexts. If you don’t need Node-specific features, consider removingnode-fetch
to reduce surface area.I can help scan the codebase for
require('node-fetch')
orimport fetch
call sites and suggest safe removal if unused.
24-32
: Native modules are correctly unpacked
Both@stoprocent/noble
and@stoprocent/bluetooth-hci-socket
are listed underasarUnpack
inelectron-builder.config.js
. BLE support will work as expected.
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (2)
packages/connect-examples/electron-example/src/index.ts (1)
279-296
: Prefer app.once('ready') to avoid accidental double init.Using once reduces the chance of re‑registration on module reloads.
- app.on('ready', () => { + app.once('ready', () => {package.json (1)
40-41
: Add CI‑safe desktop build scripts (never publish).Provide CI variants that hard‑disable publishing. Point workflows to these.
"build:example:desktop:mac": "cd ./packages/connect-examples/electron-example && yarn make:mac", "build:example:desktop:win": "cd ./packages/connect-examples/electron-example && yarn make:win", + "build:example:desktop:mac:ci": "cd ./packages/connect-examples/electron-example && cross-env ELECTRON_BUILDER_SKIP_PUBLISH=1 yarn make:mac --publish=never", + "build:example:desktop:win:ci": "cd ./packages/connect-examples/electron-example && cross-env ELECTRON_BUILDER_SKIP_PUBLISH=1 yarn make:win --publish=never",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
package.json
(3 hunks)packages/connect-examples/electron-example/src/index.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (3)
packages/connect-examples/electron-example/src/index.ts (1)
285-291
: Nice: wrap native init in try/catch with logging.This prevents hard crashes when native modules fail to load. Good call.
package.json (2)
54-55
: Useful: dev:transport-electron script.This improves local iteration on the Electron transport. No issues.
84-84
: electron-builder pin confirmed in app package; no action neededThe
packages/connect-examples/electron-example/package.json
already pins electron-builder to ^24.9.1, satisfying the suggestion to declare it in the app. Hoisting at the root poses no drift risk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (7)
.github/workflows/build-example-desktop.yml (4)
6-8
: Lock down GITHUB_TOKEN permissionsAdd minimal permissions to the job. This also resolves the CodeQL warning.
jobs: build: + permissions: + contents: read runs-on: windows-latestIf this job will publish, change to contents: write.
13-19
: Finish Yarn cache setup for a monorepoYou enabled caching but didn’t set the dependency path. Point it at all yarn.lock files to get cache hits across workspaces.
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' cache: 'yarn' + cache-dependency-path: '**/yarn.lock'
20-21
: Use user-level git config for long paths on hosted runners--system often fails. Use --global.
- - name: Enable long paths on Windows (for node-gyp) - run: git config --system core.longpaths true + - name: Enable long paths on Windows (for node-gyp) + run: git config --global core.longpaths true
36-39
: Avoid accidental releases from CIThis script likely publishes (electron-builder --publish always). Use a CI-safe script that never publishes.
- - name: Build Desktop Application (Windows) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: yarn build:example:desktop:win + - name: Build Desktop Application (Windows) [no publish] + run: yarn build:example:desktop:win:ciFollow-up: add the :ci script in package.json to pass --publish never (see comment in package.json).
packages/connect-examples/electron-example/package.json (3)
7-7
: Fix misleading package descriptionUpdate the description to match this Electron hardware example.
- "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",
18-21
: Add CI-safe make scripts (no publish)Keep make:* for local publishing, add make:*:ci that never publishes. CI should call the :ci variants.
"scripts": { "build:main": "webpack --config webpack.config.ts", "rebuild:deps": "electron-builder install-app-deps", - "make:mac": "yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", - "make:win": "yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac": "yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", + "make:win": "yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",Then update the workflow to use yarn build:example:desktop:win:ci which maps to make:win:ci.
24-33
: Ensure HCI socket is present at runtime (Linux/Electron main)noble needs @stoprocent/bluetooth-hci-socket on Linux. It won’t always install transitively, and you’ve externalized/asarUnpack’ed it in tooling. Add it explicitly to avoid module-not-found at runtime.
"dependencies": { "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4", "electron-is-dev": "^3.0.1", "electron-log": "^5.1.5", "electron-updater": "^6.2.1", "fs-extra": "^11.2.0", "node-fetch": "^2.6.7", "@onekeyfe/hd-transport-electron": "1.1.10-alpha.4" },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
.github/workflows/build-example-desktop.yml
(1 hunks)packages/connect-examples/electron-example/package.json
(2 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeQL
.github/workflows/build-example-desktop.yml
[warning] 7-46: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{contents: read}}
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build
- GitHub Check: build (22)
- GitHub Check: lint (22)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/hd-transport/package.json (1)
1-20
: Consider adding files whitelist and sideEffectsKeeps publishes lean and helps bundlers drop unused code.
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10-alpha.5", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "package.json", + "README.md", + "LICENSE" + ],
♻️ Duplicate comments (13)
packages/shared/package.json (1)
1-28
: Add files whitelist and mark as side‑effect freeRepeat of a prior suggestion: ship only built artifacts and enable tree‑shaking.
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10-alpha.5", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "package.json", + "README.md", + "LICENSE" + ],packages/hd-transport-lowlevel/package.json (1)
21-24
: Switch internal pins to workspace protocolSame feedback as earlier rounds: use workspace:* to simplify synchronized bumps across the monorepo.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.5", - "@onekeyfe/hd-transport": "1.1.10-alpha.5" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }packages/hd-transport-web-device/package.json (1)
23-24
: Switch internal deps to workspace protocol to avoid constant version churn.Use workspace protocol so bumps stay in sync across the monorepo.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.5", - "@onekeyfe/hd-transport": "1.1.10-alpha.5" + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^" }, "devDependencies": { - "@onekeyfe/hd-transport-electron": "1.1.10-alpha.5", + "@onekeyfe/hd-transport-electron": "workspace:^", "@types/w3c-web-usb": "^1.0.6", "@types/web-bluetooth": "^0.0.17" }Also applies to: 27-27
packages/connect-examples/electron-example/package.json (2)
25-26
: Add @stoprocent/bluetooth-hci-socket to dependencies for Linux HCI runtime.Without it, Electron on Linux will throw “module not found” when noble loads the HCI backend.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.5", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4", "electron-is-dev": "^3.0.1",
19-20
: Split “make” scripts for CI vs. local publishing.Prevent accidental publishing in CI by adding no-publish variants.
"scripts": { @@ "rebuild:deps": "electron-builder install-app-deps", - "make:mac": "yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", - "make:win": "yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac": "yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", + "make:win": "yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",packages/hd-transport-react-native/package.json (1)
22-23
: Use workspace protocol for internal deps.Keeps RN transport aligned with shared and base transport without manual bumps.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.5", - "@onekeyfe/hd-transport": "1.1.10-alpha.5", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" }packages/hd-ble-sdk/package.json (1)
23-26
: Prefer workspace protocol for internal deps to simplify coordinated releasesThis keeps monorepo bumps trivial and avoids accidental skew between packages.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.5", - "@onekeyfe/hd-shared": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.5" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }packages/hd-transport-electron/package.json (3)
16-21
: Rebuild native modules against Electron’s ABI@noble’s stack uses native addons. Without an Electron rebuild (or prebuilds), runtime will fail due to ABI mismatch.
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", - "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket || true" },Add devDependency:
"devDependencies": { + "electron-rebuild": "^3.6.0", "@types/web-bluetooth": "^0.0.17", "electron": "^25.0.0", "typescript": "^5.3.3" }
31-31
: Allow patch fixes for nobleUse a caret so you get non-breaking patch updates automatically.
- "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4",
27-33
: Declare Linux HCI socket as an optional peer for better DXLinux users often need the HCI socket. Declare it as an optional peer to guide them without breaking macOS/Windows installs.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },packages/hd-web-sdk/package.json (1)
24-27
: Unify internal deps via workspaces to avoid skewSwitch to workspace:* for internal packages to keep versions in lockstep during development.
"dependencies": { "@onekeyfe/cross-inpage-provider-core": "^0.0.17", - "@onekeyfe/hd-core": "1.1.10-alpha.5", - "@onekeyfe/hd-shared": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.5" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*" }packages/hd-common-connect-sdk/package.json (2)
3-3
: Ensure CHANGELOG/tag exist for 1.1.10-alpha.5Keep release notes and tags in sync with this bump.
Run this to verify:
#!/bin/bash set -euo pipefail # Check for a changelog entry (repo may keep a root or per-package changelog) rg -nP "##\\s*\\[?1\\.1\\.10-alpha\\.5\\]?" -C2 -- \ CHANGELOG.md packages/hd-common-connect-sdk/CHANGELOG.md || true # Confirm git tag exists locally (or in CI with fetch --tags before) git tag -l | rg -n "1\\.1\\.10-alpha\\.5" || true
23-29
: Adopt workspace protocol for internal dependenciesKeeps internal packages synced and reduces PR churn on version bumps.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.5", - "@onekeyfe/hd-shared": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-emulator": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-lowlevel": "1.1.10-alpha.5", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.5" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*" }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (5)
packages/hd-transport/package.json (1)
3-5
: Description fix LGTMClear, user‑facing summary. No further changes needed here.
packages/hd-ble-sdk/package.json (1)
3-3
: Version bump looks consistent with the repo-wide alpha.5 syncNo issues from this file’s perspective. Ensure the publishing pipeline includes this package in the alpha.5 set.
packages/hd-transport-electron/package.json (1)
3-3
: Version bump aligns with alpha.5 waveLooks good.
packages/hd-web-sdk/package.json (1)
3-3
: Alpha.5 version is consistentNo action needed here.
packages/connect-examples/expo-example/package.json (1)
3-3
: Version bump matches the repo-wide alpha.5All good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
♻️ Duplicate comments (10)
.github/workflows/build-example-desktop-win.yml (4)
9-11
: Reduce token permissions to read-only (no publish in CI).Use contents: read. If you must publish, do it in a dedicated release workflow.
build-win: runs-on: windows-latest - permissions: - contents: write + permissions: + contents: read
18-23
: Tighten Yarn cache key with lockfiles.Add cache-dependency-path so restores are correct across packages.
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' cache: 'yarn' + cache-dependency-path: '**/yarn.lock'
24-25
: Use user-level git longpaths.--system can fail on hosted runners. Switch to --global.
- - name: Enable long paths on Windows - run: git config --system core.longpaths true + - name: Enable long paths on Windows + run: git config --global core.longpaths true
33-38
: Build-only in CI: remove GH_TOKEN and avoid accidental Releases.Run a no-publish script (e.g., make:win:ci) and drop GH_TOKEN here.
- name: Build Windows App - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd packages/connect-examples/electron-example - yarn make:win + yarn make:win:ciAdd this script to packages/connect-examples/electron-example/package.json:
"scripts": { + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
.github/workflows/build-example-desktop-mac.yml (4)
3-5
: Quote the on key to silence yamllint truthy warning.-on: +'on': workflow_call:
9-11
: Limit GITHUB_TOKEN to read.Build-only workflows should not have write perms.
build-mac: runs-on: macos-latest - permissions: - contents: write + permissions: + contents: read
18-22
: Enable Yarn cache and scope it to lockfiles.- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' + cache: 'yarn' + cache-dependency-path: '**/yarn.lock'
29-35
: Build-only in CI: remove GH_TOKEN and avoid publish.Switch to a no-publish script (make:mac:ci). Drop GH_TOKEN.
- name: Build macOS App - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd packages/connect-examples/electron-example - yarn make:mac + yarn make:mac:ciAdd this script to packages/connect-examples/electron-example/package.json:
"scripts": { + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never",
.github/workflows/build-example-desktop.yml (2)
7-9
: Add minimal permissions to the build-mac job.Satisfies CodeQL and least-privilege.
build-mac: + permissions: + contents: read uses: ./.github/workflows/build-example-desktop-mac.yml secrets: inherit
11-13
: Add minimal permissions to the build-win job.Mirror mac job.
build-win: + permissions: + contents: read uses: ./.github/workflows/build-example-desktop-win.yml secrets: inherit
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
.github/workflows/build-example-desktop-mac.yml
(1 hunks).github/workflows/build-example-desktop-win.yml
(1 hunks).github/workflows/build-example-desktop.yml
(1 hunks)
🧰 Additional context used
🪛 YAMLlint (1.37.1)
.github/workflows/build-example-desktop-mac.yml
[warning] 3-3: truthy value should be one of [false, true]
(truthy)
[error] 41-41: too many blank lines (1 > 0)
(empty-lines)
🪛 GitHub Check: CodeQL
.github/workflows/build-example-desktop.yml
[warning] 8-11: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{}}
[warning] 12-13: Workflow does not contain permissions
Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {{}}
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (1)
.github/workflows/build-example-desktop-win.yml (1)
18-23
: Update Windows CI to existing scripts & confirm Node.js 22 readiness
- Shell output shows
electron-builder
is pinned to ^24.9.1.- Node.js 22 is LTS since October 29 2024; Electron’s ecosystem moved to Node 22 as the baseline in early 2025 (github.com, electronjs.org).
- Native modules (
@stoprocent/noble
,@stoprocent/bluetooth-hci-socket
) use N-API vianode-gyp
andnode-gyp-build
, ensuring ABI stability on Node 22 (ABI version 131) (nodejs.org, dev.to).- There’s no
make:win:ci
script inpackage.json
; replace it with the existingmake:win
.- Add
npm run rebuild:deps
before packaging to force native modules to rebuild under Node 22.Apply this patch to .github/workflows/build-example-desktop-win.yml:
- - name: Build Windows - run: npm run make:win:ci + - name: Rebuild native deps + run: npm run rebuild:deps + + - name: Build Windows + run: npm run make:winLikely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (10)
.github/workflows/build-example-desktop.yml (2)
3-4
: Quote the on key for linter parityYAML linters can flag bare on. Quote it to stay quiet.
Apply:
-on: +'on': workflow_dispatch:
11-12
: Avoid secrets: inherit unless strictly neededPassing all repo secrets increases blast radius. If called workflows don’t need any custom secrets, drop inherit. If they need a subset, pass only those explicitly in the called workflows.
Proposed change (if no secrets required):
build-mac: uses: ./.github/workflows/build-example-desktop-mac.yml - secrets: inherit build-win: uses: ./.github/workflows/build-example-desktop-win.yml - secrets: inheritPlease confirm whether the reusable workflows require GH_TOKEN or any other secrets. If not, remove inherit here and in the called files.
Also applies to: 15-16
packages/hd-transport-electron/src/utils/blePairing.ts (8)
14-18
: Make write mode configurable for WindowsWindows BLE stacks often work better with write-with-response for pairing.
export interface Step3Options { intervalMs?: number; // default 3000 maxCycles?: number; // default 10 initDataHex?: string; // hex string to write; default OneKey init + withoutResponse?: boolean; // default true; false for write-with-response }
Then update the write call at line 105:
+ const withoutResponse = options.withoutResponse ?? true; // ... - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError: any) => {
68-77
: Set resolved flag before rejectEnsure no further cycles run after timeout.
if (cycles >= maxCycles) { if (!resolved && !responseReceived) { const totalElapsed = ((Date.now() - startTime) / 1000).toFixed(1); logger?.info('[Pairing] timeout', { deviceId, cycles, elapsed: totalElapsed }); + resolved = true; - resolved = true; cleanup(); reject(ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, 'ble pairing timeout')); } return; }
91-118
: Handle sync errors from unsubscribeSome noble bindings throw synchronously when the peripheral is disconnected.
- notifyCharacteristic.unsubscribe(() => { + try { + notifyCharacteristic.unsubscribe(() => { notifyCharacteristic.subscribe((subscribeError: any) => { if (subscribeError) { logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, error: String(subscribeError ?? 'unknown error'), }); scheduleNextCycle(); // Reschedule for next attempt return; } notifyCharacteristic.once('data', onData); writeCharacteristic.write(buffer, true, (writeError: any) => { if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, error: String(writeError ?? 'unknown error'), }); } else { logger?.info('[Pairing] write', { deviceId, cycle: cycles }); } scheduleNextCycle(); // Reschedule for next attempt }); }); - }); + }); + } catch (e) { + logger?.error('[Pairing] unsubscribe threw', { + deviceId, + cycle: cycles, + error: String(e) + }); + scheduleNextCycle(); + }
43-43
: Use portable timer type
NodeJS.Timeout
clashes with DOM typings in Electron builds.- let timeoutId: NodeJS.Timeout | null = null; + let timeoutId: ReturnType<typeof setTimeout> | null = null;
47-55
: Set resolved flag before cleanupLate write callbacks can still schedule cycles after timeout because
resolved
is set after cleanup.const onData = (data: Buffer) => { if (resolved || responseReceived) return; responseReceived = true; const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); logger?.info('[Pairing] response', { deviceId, cycles, elapsed, len: data.length }); - resolved = true; - cleanup(); - resolve(); + resolved = true; + cleanup(); + resolve(); };
63-63
: Validate hex string before useInvalid hex produces an empty Buffer and causes silent retries.
Add validation before line 63:
+ // Validate hex string + const isValidHex = + typeof initDataHex === 'string' && + initDataHex.length > 0 && + (initDataHex.length % 2 === 0) && + /^[0-9a-fA-F]+$/.test(initDataHex); + + if (!isValidHex) { + logger?.error('[Pairing] invalid initDataHex', { deviceId, hex: initDataHex }); + reject(new Error('Invalid hex string for pairing')); + return; + } + const buffer = Buffer.from(initDataHex, 'hex');
57-61
: Clear timeout handle after useNull out the handle after clearing to prevent accidental reuse.
const cleanup = () => { - if (timeoutId) clearTimeout(timeoutId); + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } // Only remove the specific listener for this probe notifyCharacteristic.removeListener('data', onData); };
20-26
: Load the default Initialize payload lazilyBuilding the protobuf at module load time can throw if DataManager isn't ready. It also adds startup cost even when the probe isn't used.
-// Get protobuf definitions -const messages = DataManager.getProtobufMessages(); -const messagesRoot = parseConfigure(messages as any); - -// Dynamically build the Initialize message -const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); -const DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); +let DEFAULT_INIT_DATA: string | null = null; + +function getDefaultInitHex(): string { + if (DEFAULT_INIT_DATA) return DEFAULT_INIT_DATA; + const messages = DataManager.getProtobufMessages(); + const messagesRoot = parseConfigure(messages as any); + const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); + DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); + return DEFAULT_INIT_DATA; +}Then update line 37:
- const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const initDataHex = options.initDataHex ?? getDefaultInitHex();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
.github/workflows/build-example-desktop.yml
(1 hunks)packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (2)
.github/workflows/build-example-desktop.yml (2)
1-1
: Good rename and split to platform-specific reusable workflowsClear name and separation improve readability and maintenance.
11-16
: Confirm reusable workflow contractEnsure both .github/workflows/build-example-desktop-mac.yml and ...-win.yml are declared with on: workflow_call and define their own minimal permissions. Otherwise, these calls will fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/hd-transport-electron/src/utils/blePairing.ts (1)
1-124
: Tests: cover success, timeout, unsubscribe-throw, and cancelAdd unit tests with a fake Characteristic (EventEmitter-based) to simulate:
- subscribe error → reschedules
- write error → reschedules
- data event → resolves once and cleans up
- cycles exhausted → rejects once and stops scheduling
- unsubscribe throws → logs and reschedules
- AbortSignal abort → rejects and cleans up
I can provide a minimal fake and timer-driven harness if helpful.
♻️ Duplicate comments (7)
packages/hd-transport-electron/src/utils/blePairing.ts (7)
1-4
: Promote header to JSDoc and clarify contractMake the header a concise JSDoc. State behavior, side effects, and terminal conditions.
Apply:
-/* - * BLE Pairing Utilities - periodic pairing probe - * Unsubscribe → subscribe → write every interval until response or max cycles - */ +/** + * Periodic BLE pairing probe. + * Unsubscribe → subscribe → write until a notify response or timeout. + * Side effects: temporarily toggles notify subscription on the given characteristic. + * Resolves on first data event; rejects on timeout or when aborted. + */
20-27
: Lazy-load Initialize payload to avoid module-load side effectsBuilding protobuf at import time can throw if DataManager isn’t ready and adds startup cost. Compute on first use and cache.
Apply:
-// Get protobuf definitions -const messages = DataManager.getProtobufMessages(); -const messagesRoot = parseConfigure(messages as any); - -// Dynamically build the Initialize message -const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); -const DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); +let DEFAULT_INIT_DATA: string | null = null; +function getDefaultInitHex(): string { + if (DEFAULT_INIT_DATA) return DEFAULT_INIT_DATA; + const messages = DataManager.getProtobufMessages(); + const messagesRoot = parseConfigure(messages as any); + const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); + DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); + return DEFAULT_INIT_DATA; +} @@ - const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const initDataHex = options.initDataHex ?? getDefaultInitHex();Also applies to: 37-37
14-18
: Make write response mode configurable (Windows often prefers with-response)Expose withoutResponse to tune reliability. Keep default behavior unchanged.
Apply:
export interface Step3Options { intervalMs?: number; // default 3000 maxCycles?: number; // default 10 initDataHex?: string; // hex string to write; default OneKey init + withoutResponse?: boolean; // default true } @@ - const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const initDataHex = options.initDataHex ?? getDefaultInitHex(); + const withoutResponse = options.withoutResponse ?? true; @@ - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError: unknown) => { if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, - error: String(writeError ?? 'unknown error'), + error: String(writeError ?? 'unknown error'), }); } else { - logger?.info('[Pairing] write', { deviceId, cycle: cycles }); + logger?.info('[Pairing] write', { deviceId, cycle: cycles, withoutResponse }); } scheduleNextCycle(); // Reschedule for next attempt });Also applies to: 37-37, 105-116
63-66
: Validate initDataHex and fail fast on bad hexPrevent silent retries with an empty/invalid buffer. Reject early with a clear error.
Apply:
- const buffer = Buffer.from(initDataHex, 'hex'); + const isValidHex = + typeof initDataHex === 'string' && + initDataHex.length > 0 && + (initDataHex.length % 2 === 0) && + /^[0-9a-fA-F]+$/.test(initDataHex); + if (!isValidHex) { + logger?.error('[Pairing] invalid initDataHex', { deviceId, length: initDataHex?.length ?? 0 }); + resolved = true; + cleanup(); + reject(new Error('invalid initDataHex')); + return; + } + const buffer = Buffer.from(initDataHex, 'hex');
91-118
: Guard unsubscribe against sync throwsSome noble bindings throw synchronously on unsubscribe when disconnected. Catch and reschedule cleanly.
Apply:
- notifyCharacteristic.unsubscribe(() => { - notifyCharacteristic.subscribe((subscribeError: any) => { + try { + notifyCharacteristic.unsubscribe(() => { + notifyCharacteristic.subscribe((subscribeError: unknown) => { if (subscribeError) { logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, error: String(subscribeError ?? 'unknown error'), }); scheduleNextCycle(); // Reschedule for next attempt return; } notifyCharacteristic.once('data', onData); - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError: unknown) => { if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, error: String(writeError ?? 'unknown error'), }); } else { - logger?.info('[Pairing] write', { deviceId, cycle: cycles }); + logger?.info('[Pairing] write', { deviceId, cycle: cycles, withoutResponse }); } scheduleNextCycle(); // Reschedule for next attempt }); - }); - }); + }); + }); + } catch (e) { + logger?.error('[Pairing] unsubscribe threw', { deviceId, cycle: cycles, error: String(e) }); + scheduleNextCycle(); + }
57-61
: Unsubscribe in cleanup to leave a clean stateAvoid leaving transient subscriptions behind if the caller bails or reconfigures.
Apply:
const cleanup = () => { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } // Only remove the specific listener for this probe notifyCharacteristic.removeListener('data', onData); + try { + notifyCharacteristic.unsubscribe(() => {}); + } catch (_) { + /* noop */ + } };
10-11
: Normalize errors and avoid.message
on non-ErrorsType callback errors as unknown and convert with a helper to keep logs sane across bindings.
Apply:
-import type { Logger } from '../types/noble-extended'; +import type { Logger } from '../types/noble-extended'; +import { toErrorMessage } from '../types/noble-extended'; @@ - notifyCharacteristic.subscribe((subscribeError: any) => { + notifyCharacteristic.subscribe((subscribeError: unknown) => { if (subscribeError) { logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, - error: String(subscribeError ?? 'unknown error'), + error: toErrorMessage(subscribeError), }); scheduleNextCycle(); // Reschedule for next attempt return; } @@ - writeCharacteristic.write(buffer, withoutResponse, (writeError: unknown) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError: unknown) => { if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, - error: String(writeError ?? 'unknown error'), + error: toErrorMessage(writeError), }); } else { logger?.info('[Pairing] write', { deviceId, cycle: cycles, withoutResponse }); } scheduleNextCycle(); // Reschedule for next attempt }); @@ - } catch (e) { - logger?.error('[Pairing] unsubscribe threw', { deviceId, cycle: cycles, error: String(e) }); + } catch (e) { + logger?.error('[Pairing] unsubscribe threw', { deviceId, cycle: cycles, error: toErrorMessage(e) }); scheduleNextCycle(); }If you prefer not to import a helper, keep
String(err ?? 'unknown error')
but keep types asunknown
.Also applies to: 92-101, 105-116, 119-119
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (1)
packages/hd-transport-electron/src/utils/blePairing.ts (1)
83-87
: Good: self-scheduling prevents overlapping cyclesUsing a setTimeout-based rescheduler and short-circuiting on resolved/received avoids reentrancy and racey overlaps. Nice.
Also applies to: 121-122
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (10)
packages/hd-transport-electron/src/utils/blePairing.ts (10)
20-26
: Lazy-load DEFAULT_INIT_DATA to avoid import-time side effectsBuilding protobuf payloads at module load can throw and slows startup. Compute on first use and cache.
-// Get protobuf definitions -const messages = DataManager.getProtobufMessages(); -const messagesRoot = parseConfigure(messages as any); - -// Dynamically build the Initialize message -const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); -const DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); +let DEFAULT_INIT_DATA: string | null = null; +function getDefaultInitHex(): string { + if (DEFAULT_INIT_DATA) return DEFAULT_INIT_DATA; + const messages = DataManager.getProtobufMessages(); + const messagesRoot = parseConfigure(messages as any); + const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); + DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); + return DEFAULT_INIT_DATA; +}
37-37
: Use the lazy getter for default init hexWire the lazy loader into the options fallback.
- const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const initDataHex = options.initDataHex ?? getDefaultInitHex();
14-18
: Expose withoutResponse and AbortSignal in optionsMake write mode explicit and let callers cancel the probe (e.g., user closes modal).
export interface Step3Options { intervalMs?: number; // default 3000 maxCycles?: number; // default 10 initDataHex?: string; // hex string to write; default OneKey init + withoutResponse?: boolean; // default true + signal?: AbortSignal; // optional cancel support }
35-37
: Coerce timings; plumb signal and write modePrevent busy loops/immediate timeouts; read new options.
- const intervalMs = options.intervalMs ?? 3000; - const maxCycles = options.maxCycles ?? 10; - const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const intervalMs = Math.max(1, Number(options.intervalMs ?? 3000)); + const maxCycles = Math.max(1, Math.floor(Number(options.maxCycles ?? 10))); + const initDataHex = options.initDataHex ?? getDefaultInitHex(); + const signal = options.signal; + const withoutResponse = options.withoutResponse ?? true;
39-55
: Add abort handling to stop promptly and free resourcesSupport early cancel and ensure no further scheduling occurs.
return new Promise<void>((resolve, reject) => { let resolved = false; let responseReceived = false; let cycles = 0; let timeoutId: ReturnType<typeof setTimeout> | null = null; const startTime = Date.now(); + if (signal?.aborted) { + resolved = true; + reject(new Error('pairing_aborted')); + return; + } + + const onAbort = () => { + if (resolved) return; + resolved = true; + cleanup(); + reject(new Error('pairing_aborted')); + }; + signal?.addEventListener('abort', onAbort, { once: true }); + const onData = (data: Buffer) => { if (resolved || responseReceived) return; responseReceived = true; const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); logger?.info('[Pairing] response', { deviceId, cycles, elapsed, len: data.length }); resolved = true; cleanup(); resolve(); };
57-64
: Cleanup should also unsubscribe and remove abort listenerLeave a clean state and avoid stray notifications.
const cleanup = () => { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } - // Only remove the specific listener for this probe - notifyCharacteristic.removeListener('data', onData); + // Only remove the specific listener for this probe + notifyCharacteristic.removeListener('data', onData); + // Best effort: stop notifications started by the probe + try { + notifyCharacteristic.unsubscribe(() => {}); + } catch { + /* noop */ + } + // Detach abort handler if attached + signal?.removeEventListener('abort', onAbort as any); };
66-66
: Validate initDataHex; fail fast on bad inputInvalid hex yields empty/short buffers and confusing retries. Validate and reject early.
- const buffer = Buffer.from(initDataHex, 'hex'); + const isValidHex = + typeof initDataHex === 'string' && + initDataHex.length > 0 && + initDataHex.length % 2 === 0 && + /^[0-9a-fA-F]+$/.test(initDataHex); + if (!isValidHex) { + logger?.error('[Pairing] invalid initDataHex', { deviceId, length: (initDataHex as any)?.length ?? 0 }); + resolved = true; + cleanup(); + reject(new Error('invalid initDataHex')); + return; + } + const buffer = Buffer.from(initDataHex, 'hex');
94-121
: Wrap unsubscribe in try/catch (bindings can throw synchronously)Some noble variants throw if already disconnected. Catch and reschedule.
- notifyCharacteristic.unsubscribe(() => { - notifyCharacteristic.subscribe((subscribeError: any) => { + try { + notifyCharacteristic.unsubscribe(() => { + notifyCharacteristic.subscribe((subscribeError: unknown) => { if (subscribeError) { - logger?.error('[Pairing] subscribe failed', { + logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, - error: String(subscribeError ?? 'unknown error'), + error: toErrorMessage(subscribeError), }); scheduleNextCycle(); // Reschedule for next attempt return; } notifyCharacteristic.once('data', onData); - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError: unknown) => { if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, - error: String(writeError ?? 'unknown error'), + error: toErrorMessage(writeError), }); } else { - logger?.info('[Pairing] write', { deviceId, cycle: cycles }); + logger?.info('[Pairing] write', { deviceId, cycle: cycles, withoutResponse }); } scheduleNextCycle(); // Reschedule for next attempt }); }); - }); + }); + } catch (e) { + logger?.error('[Pairing] unsubscribe threw', { deviceId, cycle: cycles, error: toErrorMessage(e) }); + scheduleNextCycle(); + }
10-10
: Normalize callback errors; avoid.message
on non-ErrorsTreat callback errors as unknown and log robustly with a normalizer.
-import type { Logger } from '../types/noble-extended'; +import type { Logger } from '../types/noble-extended'; +import { toErrorMessage } from '../types/noble-extended'; @@ - notifyCharacteristic.subscribe((subscribeError: any) => { + notifyCharacteristic.subscribe((subscribeError?: unknown) => { @@ - error: String(subscribeError ?? 'unknown error'), + error: toErrorMessage(subscribeError), }); @@ - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError?: unknown) => { @@ - error: String(writeError ?? 'unknown error'), + error: toErrorMessage(writeError), });Also applies to: 95-101, 108-114
27-34
: Add a concise JSDoc describing contract and side effectsDocument that the probe temporarily unsubscribes/resubscribes, writes every interval until a response or timeout, logs each step, and resolves on first response or rejects on timeout/abort. This saves future spelunking.
+ +/** + * Periodic BLE pairing probe. + * + * Behavior: + * - For each cycle: unsubscribe → subscribe → attach one-time 'data' → write init payload. + * - Repeats every `intervalMs` up to `maxCycles`, or until the first notify response arrives. + * - Resolves on response. Rejects on timeout or when `options.signal` aborts. + * + * Side effects: + * - Temporarily toggles notifications on `notifyCharacteristic`. + * - Leaves notifications off on cleanup. + */
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (1)
packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: lint (22)
- GitHub Check: build (22)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
packages/hd-transport-electron/src/utils/blePairing.ts (1)
68-90
: Good sequencing: single-flight cycles and early-resolve guardUsing setTimeout to schedule the next cycle only after the write callback avoids overlapping probe cycles. The resolved/responseReceived checks also short‑circuit late timers. Nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
♻️ Duplicate comments (18)
packages/shared/package.json (1)
3-3
: Version bump looks good; add publish hygiene and tree‑shaking metadata.Add a minimal publish whitelist and mark the package as side‑effect free to keep installs lean and enable tree‑shaking.
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-electron/package.json (3)
23-26
: Declare Bluetooth HCI socket as an optional peer to guide Linux users.Most Linux setups need this, but it should remain optional for macOS/Windows.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Add electron-rebuild to ensure native modules match Electron’s ABI.Without a rebuild step, native deps (HCI socket, noble) can fail at runtime with Electron >= 25.
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket" },"devDependencies": { "@types/web-bluetooth": "^0.0.17", "electron": "^25.0.0", - "typescript": "^5.3.3" + "typescript": "^5.3.3", + "electron-rebuild": "^3.6.0" }Run this script to verify there are no leftover abandonware imports and that a rebuild hook exists:
#!/usr/bin/env bash set -euo pipefail echo "1) Ensure only @stoprocent noble is referenced" rg -nP "@abandonware/noble" -C2 || echo "OK: no @abandonware/noble references" echo "2) Check for electron-rebuild or prebuild hooks" rg -nP 'electron-rebuild|prebuild' -C2 || echo "WARN: no rebuild/prebuild hooks found" echo "3) Spot any direct imports of @stoprocent/bluetooth-hci-socket" rg -nP "from ['\"]@stoprocent/bluetooth-hci-socket['\"]|require\\(['\"]@stoprocent/bluetooth-hci-socket['\"]\\)" -C2 || echo "OK: no direct imports (kept optional)"Also applies to: 34-38
27-33
: Loosen noble to pick up patch fixes automatically.Use a caret for @stoprocent/noble so you receive patch updates without manual bumps.
"dependencies": { "@onekeyfe/hd-core": "1.1.10", "@onekeyfe/hd-shared": "1.1.10", "@onekeyfe/hd-transport": "1.1.10", - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" },packages/hd-transport-lowlevel/package.json (1)
22-23
: Consider workspace protocol for internal deps to ease future bumps.Switching to workspace:* reduces churn when you cut new releases.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }packages/hd-transport-react-native/package.json (1)
22-23
: Use workspace protocol for internal deps for smoother releases.This keeps RN package aligned automatically with the monorepo versions.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" }packages/connect-examples/electron-example/package.json (4)
32-32
: Drop node-fetch; use global fetch (Node 18+/Electron)Electron 28 ships Node 18+. Prefer the built-in Fetch API to reduce deps and native rebuilds.
- "node-fetch": "^2.6.7"
Follow-up: replace imports with global fetch (or undici if you need specific features).
18-20
: Add CI-safe build scripts to avoid accidental publishingProvide
make:*:ci
variants that never publish artifacts in CI."scripts": { "build:main": "webpack --config webpack.config.ts", "rebuild:deps": "electron-builder install-app-deps", - "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", - "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", + "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
25-27
: Missing HCI socket dependency will break Linux BLE at runtime
@stoprocent/bluetooth-hci-socket
is optional in noble and often not installed; add it explicitly to avoid module-not-found in Electron main on Linux."dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "2.2.3",
7-7
: Fix the package descriptionThis is an Electron hardware example, not “End-to-end encrypted workspaces”.
- "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",packages/hd-transport-http/package.json (1)
27-31
: Upgrade axios to patched 1.x; 0.27.x carries known vulnsBump axios to a secure 1.x (e.g., ^1.11.0). Run tests to catch breaking changes.
"dependencies": { "@onekeyfe/hd-shared": "1.1.10", "@onekeyfe/hd-transport": "1.1.10", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Verification script:
#!/bin/bash set -euo pipefail cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm ls axios --depth=0 || true npm audit --omit=dev || truepackages/hd-transport-emulator/package.json (1)
27-31
: Patch axios here too to keep security posture consistentMirror the axios upgrade to ^1.11.0 in the emulator transport.
"dependencies": { "@onekeyfe/hd-shared": "1.1.10", "@onekeyfe/hd-transport": "1.1.10", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Quick checks:
#!/bin/bash set -euo pipefail cd packages/hd-transport-emulator npm install --package-lock-only --legacy-peer-deps npm ls axios --depth=0 || true npm audit --omit=dev || truepackages/hd-ble-sdk/package.json (1)
23-25
: Optional: use workspace protocol for internal depsThis eases coordinated bumps across the monorepo.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-react-native": "1.1.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }packages/core/package.json (1)
28-29
: Optional: switch internal deps to workspace protocolUsing workspace:^ keeps internal packages in sync and reduces manual bumps.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^", "axios": "^0.27.2",packages/hd-web-sdk/package.json (1)
24-27
: Optional: adopt workspace protocol for internal depsKeeps the monorepo in lockstep without manual version churn.
"dependencies": { "@onekeyfe/cross-inpage-provider-core": "^0.0.17", - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-http": "1.1.10", - "@onekeyfe/hd-transport-web-device": "1.1.10" + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport-http": "workspace:^", + "@onekeyfe/hd-transport-web-device": "workspace:^" }packages/hd-transport-web-device/package.json (1)
23-24
: Workspace protocol optional; also verify 1.1.10 publishes (incl. devDep)
- Consider workspace:^ for internal deps to avoid manual bumps.
- Confirm @onekeyfe/[email protected] is published (dev installs/builds rely on it).
Refactor:
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10" + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^" }, "devDependencies": { - "@onekeyfe/hd-transport-electron": "1.1.10", + "@onekeyfe/hd-transport-electron": "workspace:^",Publish check:
#!/bin/bash for p in \ "@onekeyfe/hd-shared" \ "@onekeyfe/hd-transport" \ "@onekeyfe/hd-transport-electron" do echo -n "$p@1.1.10 => " npm view "$p@1.1.10" version --silent || echo "NOT PUBLISHED" doneAlso applies to: 27-27
packages/hd-common-connect-sdk/package.json (1)
23-28
: Confirm all internal deps are published at 1.1.10; consider workspaces
- Verify publish status to prevent install errors.
- Optional: switch to workspace:^ to minimize manual bumps.
Publish check:
#!/bin/bash pkgs=( "@onekeyfe/hd-core" "@onekeyfe/hd-shared" "@onekeyfe/hd-transport-emulator" "@onekeyfe/hd-transport-http" "@onekeyfe/hd-transport-lowlevel" "@onekeyfe/hd-transport-web-device" ) for p in "${pkgs[@]}"; do echo -n "$p@1.1.10 => " npm view "$p@1.1.10" version --silent || echo "NOT PUBLISHED" doneWorkspace option:
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-emulator": "1.1.10", - "@onekeyfe/hd-transport-http": "1.1.10", - "@onekeyfe/hd-transport-lowlevel": "1.1.10", - "@onekeyfe/hd-transport-web-device": "1.1.10" + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport-emulator": "workspace:^", + "@onekeyfe/hd-transport-http": "workspace:^", + "@onekeyfe/hd-transport-lowlevel": "workspace:^", + "@onekeyfe/hd-transport-web-device": "workspace:^" }packages/connect-examples/expo-example/package.json (1)
10-10
: Use a single cross-env in build scriptOne cross-env can set all env vars; it’s cleaner.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (6)
packages/hd-transport-electron/package.json (1)
3-3
: Version alignment to 1.1.10 looks consistent across the stack.No concerns on the version bump here.
packages/hd-transport-lowlevel/package.json (1)
3-3
: Version bump to 1.1.10 looks consistent.All good on the version alignment.
packages/hd-transport-react-native/package.json (1)
3-3
: Stable 1.1.10 version looks good.No issues with the version bump here.
packages/connect-examples/electron-example/package.json (1)
5-5
: Version bump looks goodStable 1.1.10 aligns with the monorepo updates.
packages/connect-examples/expo-playground/package.json (1)
3-3
: Version bump to 1.1.10 looks goodMatches the rest of the workspace.
packages/hd-ble-sdk/package.json (1)
3-3
: Version alignment to 1.1.10 looks goodNo issues spotted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
♻️ Duplicate comments (18)
packages/shared/package.json (1)
3-3
: LGTM on the version bump. Consider adding publish hygiene.Looks good. To keep installs lean and enable tree‑shaking, add sideEffects: false and a files whitelist.
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10-alpha.6", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport/package.json (1)
3-4
: Nice: real description and aligned version. Add sideEffects/files for slimmer publishes.Good cleanup. Repeat suggestion: mark as side‑effect free and whitelist published files.
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10-alpha.6", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-react-native/package.json (1)
22-23
: Internal deps: prefer workspace protocol to reduce manual bumps.This keeps packages in lockstep in the monorepo.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.6", - "@onekeyfe/hd-transport": "1.1.10-alpha.6", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" }Optionally add publish hygiene too:
"types": "dist/index.d.ts", + "sideEffects": false, + "files": ["dist/**/*","README.md","LICENSE","package.json"],packages/core/package.json (1)
28-29
: Ensure deps are published or use workspace protocol.Alpha.6 pins are fine if they’re published. Otherwise, workspace protocol avoids churn inside the mono.
Verification:
#!/bin/bash set -euo pipefail for p in "@onekeyfe/hd-shared" "@onekeyfe/hd-transport"; do echo -n "$p@1.1.10-alpha.6 => " npm view "$p@1.1.10-alpha.6" version --silent || echo "NOT PUBLISHED" doneOptional workspace switch:
- "@onekeyfe/hd-shared": "1.1.10-alpha.6", - "@onekeyfe/hd-transport": "1.1.10-alpha.6", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^",packages/hd-ble-sdk/package.json (1)
23-25
: Optional: switch internal deps to workspace protocol for easier bumps.Keeps packages in lockstep without manual edits. Mirrors earlier feedback.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.6", - "@onekeyfe/hd-shared": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.6" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }packages/hd-transport-lowlevel/package.json (1)
22-23
: Optional: use workspace: for internal deps.*Reduces churn in future coordinated releases. Reiterating earlier note.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.6", - "@onekeyfe/hd-transport": "1.1.10-alpha.6" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }packages/hd-transport-http/package.json (1)
26-31
: Upgrade axios to a secure 1.x line.axios ^0.27.2 carries known advisories; prior reviews flagged this. Please bump to a patched 1.x and run tests/audit.
Apply:
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.6", "@onekeyfe/hd-transport": "1.1.10-alpha.6", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Then verify:
#!/bin/bash set -euo pipefail cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm ls axios --depth=0 || true npm audit --omit=dev || true npm run -s build || truepackages/hd-transport-emulator/package.json (1)
26-31
: Mirror the axios upgrade here to keep security posture consistent.Still on ^0.27.2. Please bump to 1.x and re-run tests/audit. This echoes prior guidance.
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.6", "@onekeyfe/hd-transport": "1.1.10-alpha.6", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Quick check:
#!/bin/bash set -euo pipefail cd packages/hd-transport-emulator npm install --package-lock-only --legacy-peer-deps npm ls axios --depth=0 || true npm audit --omit=dev || true npm run -s build || truepackages/hd-transport-electron/package.json (3)
22-26
: Declare HCI socket as an optional peer for Linux users.Guides integrators without forcing installs on macOS/Windows. Reiterating previous suggestion.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Add postinstall rebuild for native modules under Electron.Noble/HCI often needs ABI rebuilds for Electron. Add an electron-rebuild hook to avoid runtime load errors. Echoing prior advice.
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket || true", "lint": "eslint .", "lint:fix": "eslint . --fix" },
Verification steps (run in electron package and in the example app):
#!/bin/bash set -euo pipefail # 1) Ensure electron-rebuild is available (either devDep here or in root) npm exec -c "electron-rebuild --version" || echo "electron-rebuild not found" # 2) Rebuild natives for current Electron npm run -s postinstall || true # 3) Sanity: check for native bindings presence rg -nP "(?i)node-addon-api|napi|.node\\b" -C2 || true
28-33
: Use a caret for Noble to get patch fixes automatically.Avoids pinning to a single patch unless you need strict reproducibility. Mirrors an earlier note.
"dependencies": { "@onekeyfe/hd-core": "1.1.10-alpha.6", "@onekeyfe/hd-shared": "1.1.10-alpha.6", "@onekeyfe/hd-transport": "1.1.10-alpha.6", - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" }packages/hd-web-sdk/package.json (1)
24-27
: Align internal deps via workspace protocol to cut version-churn.Use workspace:* so these stay in lockstep across the monorepo.
- "@onekeyfe/hd-core": "1.1.10-alpha.6", - "@onekeyfe/hd-shared": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.6" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*"packages/connect-examples/expo-example/package.json (1)
10-11
: Use a single cross-env invocation.Simpler and clearer. Keeps the same behavior.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",packages/hd-common-connect-sdk/package.json (1)
23-28
: Adopt workspace protocol for all internal deps.Reduces manual version bumps and mismatch risk.
- "@onekeyfe/hd-core": "1.1.10-alpha.6", - "@onekeyfe/hd-shared": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-emulator": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-lowlevel": "1.1.10-alpha.6", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.6" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*"packages/connect-examples/electron-example/package.json (4)
32-32
: Drop node-fetch; use global fetch (Electron/Node ≥18).Reduces deps and native rebuilds. Only keep if you rely on v2-specific APIs.
- "node-fetch": "^2.6.7"
7-7
: Fix the package description.This is an Electron hardware example, not “End-to-end encrypted workspaces.”
- "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",
18-21
: Add CI-safe build scripts that never publish.Keep make:* for local publish. Add make:*:ci to build-only.
"scripts": { @@ "rebuild:deps": "electron-builder install-app-deps", "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
25-27
: Add @stoprocent/bluetooth-hci-socket explicitly.Noble needs the HCI socket on Linux. Adding it avoids “module not found” at runtime.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.6", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build (22)
🔇 Additional comments (10)
packages/hd-transport-react-native/package.json (1)
3-3
: Version alignment looks good.No issues spotted with the bump.
packages/hd-ble-sdk/package.json (1)
3-3
: Version alignment LGTM.Bump to 1.1.10-alpha.6 matches the monorepo. No issues spotted.
packages/hd-transport-lowlevel/package.json (1)
3-3
: Version bump looks good.Matches the coordinated alpha.6 rollout.
packages/hd-transport-http/package.json (1)
3-3
: Version alignment LGTM.Package version and internal deps align with alpha.6.
packages/hd-transport-emulator/package.json (1)
3-3
: Version update LGTM.Consistent with the alpha.6 sweep.
packages/hd-transport-electron/package.json (1)
3-3
: Version alignment LGTM.Matches the monorepo alpha.6 series.
packages/hd-web-sdk/package.json (1)
3-3
: Version bump looks consistent.Nice alignment with sibling packages.
packages/hd-transport-web-device/package.json (1)
3-3
: Version alignment looks good.No issues spotted with the bump.
packages/hd-common-connect-sdk/package.json (1)
3-3
: Version bump is consistent.All good here.
packages/connect-examples/electron-example/package.json (1)
25-26
: Native modules are correctly externalized and unpackedWebpack externals include both
@stoprocent/noble
and@stoprocent/bluetooth-hci-socket
Electron-builder’sasarUnpack
covers the same modulesNo further changes needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
♻️ Duplicate comments (18)
package.json (2)
34-34
: Make Lerna publish non-interactive in CIAvoid CI hangs by adding --yes to lerna publish.
- "publish-packages": "yarn check-versions && lerna publish from-package", + "publish-packages": "yarn check-versions && lerna publish from-package --yes",
40-41
: Add CI-safe desktop build scripts (no publish)Provide explicit CI variants to prevent accidental publishing from CI runners.
"build:example:desktop:mac": "cd ./packages/connect-examples/electron-example && yarn make:mac", "build:example:desktop:win": "cd ./packages/connect-examples/electron-example && yarn make:win", + "build:example:desktop:mac:ci": "cd ./packages/connect-examples/electron-example && yarn make:mac:ci", + "build:example:desktop:win:ci": "cd ./packages/connect-examples/electron-example && yarn make:win:ci",Follow-up: update the workflow to call the :ci scripts.
packages/hd-transport-electron/src/utils/blePairing.ts (5)
29-35
: Drop unnecessary asyncThe function manually constructs a Promise. Remove async for clarity.
-export async function runPairingProbe( +export function runPairingProbe(
86-88
: Validate initDataHex; fail fast on bad hexInvalid hex yields empty/short buffers and silent retries. Validate and reject early.
- const buffer = Buffer.from(initDataHex, 'hex'); + const isValidHex = + typeof initDataHex === 'string' && + initDataHex.length > 0 && + initDataHex.length % 2 === 0 && + /^[0-9a-fA-F]+$/.test(initDataHex); + if (!isValidHex) { + logger?.error('[Pairing] invalid initDataHex', { deviceId, len: initDataHex?.length ?? 0 }); + cleanup(); + reject(ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, 'invalid initDataHex')); + return; + } + const buffer = Buffer.from(initDataHex, 'hex');
73-73
: Add once:true to abort listenerPrevents dangling listeners if cleanup is missed.
- abortSignal?.addEventListener('abort', onAbort); + abortSignal?.addEventListener('abort', onAbort, { once: true });
75-84
: Consider unsubscribing in cleanup to leave a clean stateIf callers always re-subscribe, unsubscribe here to prevent transient double subscriptions.
const cleanup = () => { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } // Only remove the specific listener for this probe notifyCharacteristic.removeListener('data', onData); // Remove abort listener abortSignal?.removeEventListener('abort', onAbort); + try { + // Best-effort unsubscribe; ignore errors + (notifyCharacteristic as any)?.unsubscribe?.(() => {}); + } catch {} };
114-141
: Guard unsubscribe against sync throws; normalize subscribe errorsSome bindings throw synchronously on unsubscribe. Catch and reschedule. Also treat callback errors as unknown.
- notifyCharacteristic.unsubscribe(() => { - notifyCharacteristic.subscribe((subscribeError: any) => { + try { + notifyCharacteristic.unsubscribe(() => { + notifyCharacteristic.subscribe((subscribeError: unknown) => { if (subscribeError) { logger?.error('[Pairing] subscribe failed', { deviceId, cycle: cycles, - error: String(subscribeError ?? 'unknown error'), + error: String((subscribeError as any)?.message ?? subscribeError ?? 'unknown error'), }); scheduleNextCycle(); // Reschedule for next attempt return; } notifyCharacteristic.once('data', onData); - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, true, (writeError: unknown) => { if (writeError) { logger?.error('[Pairing] write failed', { deviceId, cycle: cycles, - error: String(writeError ?? 'unknown error'), + error: String((writeError as any)?.message ?? writeError ?? 'unknown error'), }); } else { logger?.info('[Pairing] write', { deviceId, cycle: cycles }); } scheduleNextCycle(); // Reschedule for next attempt }); }); - }); + } catch (e) { + logger?.error('[Pairing] unsubscribe threw', { deviceId, cycle: cycles, error: String(e) }); + scheduleNextCycle(); + }packages/hd-transport-electron/src/noble-ble-handler.ts (11)
983-996
: Connect callback: normalize error before constructing TypedErrorAvoid .message on unknown error shapes.
- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { @@ - if (error) { + if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; }
27-27
: Typo in comment“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
118-123
: Reduce notification log noise to debugPer-notification info logs are noisy and can hurt perf. Use debug level via safeLog.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, });
724-729
: Force reconnect: normalize error messageAvoid accessing .message on non-Error.
- logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + logger?.error('[NobleBLE] Force reconnect failed:', error); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));
775-778
: Fresh peripheral connect: normalize error messageSame normalization here.
- if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + if (error) { + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`));
1381-1395
: Gate console logs behind a debug flag or use loggerReduce noise and keep logs consistent.
- // Use console.log for initial logging as electron-log might not be available yet. - console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + // Minimal bootstrap log before electron-log is available. + if (process.env.ONEKEY_BLE_DEBUG) { + // eslint-disable-next-line no-console + console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + } @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) { + // eslint-disable-next-line no-console + console.log('[NobleBLE] NOBLE bootstrap'); + } @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + }
1448-1449
: IPC write log: report bytes, not hex charshexData.length counts nibbles. Divide by 2.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
1184-1189
: Guard against subscribe/unsubscribe racesIf state is 'unsubscribing', wait before subscribing to avoid races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1124-1133
: Do not log full chunk payloadsFull hex dumps can leak data and bloat logs. Log a preview and sizes only.
- logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), + chunkPreview: `${preview}...`, withResponse: false, });
1311-1324
: Probe write: comment contradicts code; also normalize error typeComment says “doesn't expect a response” but code uses with-response (false). Use withoutResponse=true or fix the comment. Normalize error logging.
- // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. - // This is a dummy write operation that doesn't expect a response. + // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. + // This is a dummy write operation; no response is expected. logger?.info(`[NobleBLE] Performing probe write to trigger pairing on ${process.platform}`); const probeData = Buffer.from('01', 'hex'); // A single byte is sufficient await new Promise<void>((resolve, reject) => { - characteristics.write.write(probeData, false, error => { - if (error) { + characteristics.write.write(probeData, true, (error: unknown) => { + if (error) { // We can often ignore errors here, as the goal is just to trigger the dialog. // However, log it for debugging purposes. - logger?.error('[NobleBLE] Probe write failed (this may be expected):', error.message); + logger?.debug('[NobleBLE] Probe write failed (this may be expected):', toErrorMessage(error)); } resolve(); // Resolve regardless of error }); });
100-100
: Action Required: Make CONNECTION_TIMEOUT configurable and update defaultVerified that
CONNECTION_TIMEOUT
is hardcoded to 3 000 ms at line 100 inpackages/hd-transport-electron/src/noble-ble-handler.ts
. There’s no existing environment override in this file. A 3 s timeout will likely fire before a full 30 s pairing window completes.• Location:
packages/hd-transport-electron/src/noble-ble-handler.ts:100
• Noprocess.env
usage found for BLE timeoutsSuggested refactor:
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number( + process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 30000 +); // default to 30 seconds for device connectionPlease confirm with the firmware/device team that 30 000 ms aligns with the auto-pairing timeout. If their spec differs, choose a safe default that won’t preempt the probe.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (4)
package.json
(4 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(25 hunks)packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)packages/shared/src/constants.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (2)
packages/shared/src/constants.ts (1)
42-42
: New IPC key for cancel pairing: LGTMEnum entry NOBLE_BLE_CANCEL_PAIRING looks good and matches the handler added in noble-ble-handler.ts. No issues.
packages/hd-transport-electron/src/noble-ble-handler.ts (1)
21-21
: Ensure TypeScript types for ‘@stoprocent/noble’I didn’t find any local shim or built-in declarations for
@stoprocent/noble
. Please verify that the package actually ships TypeScript types—or else import types from the officialnoble
package or add your own shim.• Check that
@stoprocent/noble
’s package.json has a"types"
field or anindex.d.ts
in its npm bundle.
• If it doesn’t, switch the import to pull types fromnoble
at compile-time and keeprequire('@stoprocent/noble')
at runtime.
• Alternatively, add a local declaration file (e.g.types/stoprocent-noble.d.ts
) with:declare module '@stoprocent/noble';Suggested diff:
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/connect-examples/electron-example/package.json (1)
32-32
: Remove node-fetch v2 (security + redundancy)Node 18+/Electron has global fetch. node-fetch v2 carries known CVEs and native rebuilds.
- "node-fetch": "^2.6.7" + // Use global fetch (Node 18+/Electron); no dependency neededFollow-up: replace any node-fetch imports with global fetch/Request/Response.
packages/hd-transport-electron/src/noble-ble-handler.ts (1)
440-451
: Memory leak: discover listener not removed on timeouton timeout path you don't remove the 'discover' listener. Clean up consistently.
- return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - if (noble) { - noble.stopScanning(); - } - logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); - resolve(null); - }, FAST_SCAN_TIMEOUT); + return new Promise((resolve, reject) => { + const cleanup = () => { + if (!noble) return; + noble.stopScanning(); + noble.removeListener('discover', onDiscover); + }; + const timeout = setTimeout(() => { + cleanup(); + logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); + resolve(null); + }, FAST_SCAN_TIMEOUT); @@ - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + if (noble) noble.on('discover', onDiscover);Also applies to: 472-476
♻️ Duplicate comments (35)
packages/hd-transport/package.json (1)
3-4
: Nice description update; add sideEffects and files for lean publishes.Mark the package side‑effect free and whitelist publish files to reduce install size and improve tree‑shaking.
Apply:
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10-alpha.7", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ], "author": "OneKey",package.json (1)
33-35
: Make publish non‑interactive and add CI‑safe desktop build scripts.Avoid stuck CI and accidental publishes from CI runs.
Apply:
"scripts": { @@ - "check-versions": "node scripts/check-versions.js", - "publish-packages": "yarn check-versions && lerna publish from-package", + "check-versions": "node scripts/check-versions.js", + "publish-packages": "yarn check-versions && lerna publish from-package --yes", @@ - "build:example:desktop:mac": "cd ./packages/connect-examples/electron-example && yarn make:mac", - "build:example:desktop:win": "cd ./packages/connect-examples/electron-example && yarn make:win", + "build:example:desktop:mac": "cd ./packages/connect-examples/electron-example && yarn make:mac", + "build:example:desktop:win": "cd ./packages/connect-examples/electron-example && yarn make:win", + "build:example:desktop:mac:ci": "cd ./packages/connect-examples/electron-example && cross-env ELECTRON_BUILDER_SKIP_PUBLISH=1 yarn make:mac", + "build:example:desktop:win:ci": "cd ./packages/connect-examples/electron-example && cross-env ELECTRON_BUILDER_SKIP_PUBLISH=1 yarn make:win", @@ - "dev:transport-electron": "cd ./packages/hd-transport-electron && yarn dev", + "dev:transport-electron": "cd ./packages/hd-transport-electron && yarn dev",Run:
#!/bin/bash # Ensure pre-commit runs version checks. test -f .husky/pre-commit && cat .husky/pre-commit # Dry-run the version checker. node scripts/check-versions.js || trueAlso applies to: 40-41, 54-54
packages/shared/package.json (1)
3-3
: Version bump OK; add sideEffects and a files whitelist.Keep the shared package light and tree‑shakable.
Apply:
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10-alpha.7", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/connect-examples/expo-playground/package.json (1)
3-3
: Switch to workspace protocol for local packagesNone of the 1.1.10-alpha.7 tags are published on npm, so the Expo Playground example will fail to install. In packages/connect-examples/expo-playground/package.json, update the three local deps under dependencies:
--- a/packages/connect-examples/expo-playground/package.json +++ b/packages/connect-examples/expo-playground/package.json @@ -20,7 +20,7 @@ "dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.7", - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-web-sdk": "1.1.10-alpha.7", + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-web-sdk": "workspace:^", "expo": "^48.0.0", "react": "^18.0.0", "react-native": "0.71.8"This switch ensures the example installs cleanly against your local packages.
packages/hd-transport-lowlevel/package.json (1)
22-23
: Consider workspace protocol for internal depsSwitching internal packages to "workspace:*" simplifies future bumps and avoids manual churn. This mirrors prior feedback.
Apply:
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-transport": "1.1.10-alpha.7" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }packages/hd-transport-react-native/package.json (1)
22-23
: Use workspace protocol for internal depsRepeat of earlier suggestion: prefer "workspace:" for @onekeyfe/ internals to reduce manual version churn.
Apply:
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-transport": "1.1.10-alpha.7", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" }packages/hd-transport-electron/package.json (3)
23-26
: Add optional peer for @stoprocent/bluetooth-hci-socket (Linux)Guide Linux integrators without forcing installs on macOS/Windows. Mirrors prior feedback.
Apply:
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Ensure native modules rebuild for Electron >=25Noble’s stack may include native addons. Add a postinstall rebuild to avoid ABI mismatches.
Apply:
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket" },Run to check for existing rebuild hooks/imports:
#!/bin/bash set -euo pipefail rg -nP 'electron-rebuild|prebuild' -C2 || true rg -n '@stoprocent/bluetooth-hci-socket' -C2 || true
31-31
: Use a caret for noble to pick up patch fixesLet patch updates flow unless you have a known regression. This was suggested before.
Apply:
- "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4",packages/core/package.json (2)
28-29
: Optional: switch internal deps to workspace protocol.Cuts manual version churn across releases.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-transport": "1.1.10-alpha.7", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^",
3-4
: Optional publish hygiene: add sideEffects/files.Helps tree‑shaking and trims the tarball.
"description": "Core processes and APIs for communicating with OneKey hardware devices.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-ble-sdk/package.json (1)
23-25
: Consider workspace protocol for internal deps.Keeps packages in sync during dev.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.7", - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.7" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }packages/hd-transport-web-device/package.json (1)
23-24
: Optional: workspace protocol for internal deps.Reduces version churn.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-transport": "1.1.10-alpha.7" + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^" },packages/connect-examples/expo-example/package.json (1)
10-10
: Use a single cross-env.No need to chain cross-env twice. This is cleaner.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",packages/hd-common-connect-sdk/package.json (2)
3-3
: Add CHANGELOG entry and tag for 1.1.10-alpha.7Keep release bookkeeping in sync with the version bump.
Run to verify:
#!/bin/bash # From repo root rg -n "## \\[1\\.1\\.10-alpha\\.7\\]" --glob '!**/node_modules/**' || echo "Missing CHANGELOG entry" git tag -l | rg "^1\\.1\\.10-alpha\\.7$" || echo "Missing git tag 1.1.10-alpha.7"
23-28
: Prefer workspace protocol for internal depsReduces churn across the monorepo and keeps versions aligned.
- "@onekeyfe/hd-core": "1.1.10-alpha.7", - "@onekeyfe/hd-shared": "1.1.10-alpha.7", - "@onekeyfe/hd-transport-emulator": "1.1.10-alpha.7", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.7", - "@onekeyfe/hd-transport-lowlevel": "1.1.10-alpha.7", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.7" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*"packages/hd-transport-electron/src/utils/blePairing.ts (4)
21-27
: Lazy-load DEFAULT_INIT_DATA (avoid module-load side effects)Defers protobuf parsing/build until needed; safer and faster on load.
-// Get protobuf definitions -const messages = DataManager.getProtobufMessages(); -const messagesRoot = parseConfigure(messages as any); - -// Dynamically build the Initialize message -const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); -const DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); +let DEFAULT_INIT_DATA: string | null = null; +function getDefaultInitHex(): string { + if (DEFAULT_INIT_DATA) return DEFAULT_INIT_DATA; + const messages = DataManager.getProtobufMessages(); + const messagesRoot = parseConfigure(messages as any); + const initializeMessageBuffer = buildOne(messagesRoot, 'Initialize', {}); + DEFAULT_INIT_DATA = initializeMessageBuffer.toString('hex'); + return DEFAULT_INIT_DATA; +} @@ - const initDataHex = options.initDataHex ?? DEFAULT_INIT_DATA; + const initDataHex = options.initDataHex ?? getDefaultInitHex();Also applies to: 38-38
86-86
: Validate initDataHex before Buffer.fromFail fast on bad hex; avoids silent retries.
- const buffer = Buffer.from(initDataHex, 'hex'); + const isValidHex = + typeof initDataHex === 'string' && + initDataHex.length > 0 && + (initDataHex.length % 2 === 0) && + /^[0-9a-fA-F]+$/.test(initDataHex); + if (!isValidHex) { + logger?.error('[Pairing] invalid initDataHex', { deviceId, len: initDataHex?.length ?? 0 }); + resolved = true; + cleanup(); + reject(new Error('invalid initDataHex')); + return; + } + const buffer = Buffer.from(initDataHex, 'hex');
75-84
: Unsubscribe in cleanup to leave a clean statePrevents lingering subscriptions if caller exits early.
const cleanup = () => { if (timeoutId) { clearTimeout(timeoutId); timeoutId = null; } // Only remove the specific listener for this probe notifyCharacteristic.removeListener('data', onData); // Remove abort listener abortSignal?.removeEventListener('abort', onAbort); + try { + notifyCharacteristic.unsubscribe(() => {}); + } catch { /* noop */ } };
14-19
: Make write response mode configurableExpose with/without response as an option; default stays as-is.
export interface Step3Options { intervalMs?: number; // default 3000 maxCycles?: number; // default 10 initDataHex?: string; // hex string to write; default OneKey init - abortSignal?: AbortSignal; // signal to cancel the pairing probe + abortSignal?: AbortSignal; // signal to cancel the pairing probe + withoutResponse?: boolean; // default true } @@ - const { abortSignal } = options; + const { abortSignal } = options; + const withoutResponse = options.withoutResponse ?? true; @@ - writeCharacteristic.write(buffer, true, (writeError: any) => { + writeCharacteristic.write(buffer, withoutResponse, (writeError: any) => {Also applies to: 128-139
packages/connect-examples/electron-example/package.json (3)
7-7
: Fix package descriptionMake it match the actual app.
- "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",
25-27
: Ensure HCI socket is installed (Linux/Electron)Add the native HCI socket explicitly to avoid runtime module-not-found.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.7", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3",
19-20
: Add non-publishing CI build scriptsPrevents accidental publishes from CI.
"scripts": { "rebuild:deps": "electron-builder install-app-deps", "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",packages/hd-transport-electron/src/noble-ble-handler.ts (12)
95-101
: Connection timeout is likely too short; make it configurable3s may fire before Windows pairing completes. Default to 30s or env override.
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 30000);
704-731
: Use toErrorMessage in reconnect flowsAvoid unsafe
.message
and keep error text consistent.- logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + logger?.error('[NobleBLE] Force reconnect failed:', error); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));Similarly for "Fresh peripheral connection failed".
Also applies to: 741-783
21-21
: Type imports: avoid relying on @stoprocent/noble for d.tsSome forks don’t ship types. Import types from 'noble' or add a shim.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you prefer staying on @stoprocent/noble, add a local .d.ts that re-exports noble types.
118-123
: Reduce notification log noiseUse debug to avoid flooding logs and hurting perf.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, });
621-627
: Normalize callback errors; avoid.message
on non-ErrorsNoble may pass strings. Use a safe normalizer everywhere.
+import { safeLog, toErrorMessage } from './types/noble-extended'; @@ - (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { - logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + logger?.error('[NobleBLE] Service discovery failed:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; } @@ - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; } @@ - peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { @@ - if (error) { + if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; }Apply the same to force reconnect, fresh peripheral connect, chunk write reject, and unsubscribe paths shown in the ranges above.
Also applies to: 642-647, 724-729, 775-778, 990-996, 1129-1135, 1359-1365
1075-1095
: Write path: safer defaults and less-sensitive logs
- Consider with-response for control packets.
- Don’t log full chunk hex; log a short preview.
- logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length }); + const withoutResponse = false; // safer for small control writes + logger?.info('[NobleBLE] Write single', { deviceId, size: buffer.length, withoutResponse }); @@ - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => {- logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), + chunkPreview: `${preview}...`, withResponse: false, }); @@ - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, true, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); - reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, toErrorMessage(error))); return; }Also applies to: 1124-1131
1182-1189
: Guard subscribe against in-flight unsubscribeWait until operation state is idle to prevent races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1231-1263
: Normalize (un)subscribe callback errors and fix probe write semanticsHandle unknown errors, and align comment with write flag.
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + }); @@ - // On non-Windows platforms, a probe write is needed to trigger the pairing dialog. - // This is a dummy write operation that doesn't expect a response. + // On non-Windows platforms, a probe write can trigger the pairing dialog. + // This is a dummy write; we don't require a response. logger?.info(`[NobleBLE] Performing probe write to trigger pairing on ${process.platform}`); const probeData = Buffer.from('01', 'hex'); // A single byte is sufficient await new Promise<void>((resolve, reject) => { - characteristics.write.write(probeData, false, error => { + characteristics.write.write(probeData, true, (error: unknown) => { if (error) { - // We can often ignore errors here, as the goal is just to trigger the dialog. - // However, log it for debugging purposes. - logger?.error('[NobleBLE] Probe write failed (this may be expected):', error.message); + // Often ignorable; log for diagnostics. + logger?.debug('[NobleBLE] Probe write failed (expected on some stacks):', toErrorMessage(error)); } resolve(); // Resolve regardless of error }); });Also applies to: 1294-1324
1381-1395
: Replace stray console.log with gated debugKeep production logs consistent and quiet by default.
- // Use console.log for initial logging as electron-log might not be available yet. - console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + // Minimal bootstrap log before electron-log is available. + if (process.env.ONEKEY_BLE_DEBUG) { + // eslint-disable-next-line no-console + console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + } @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) { + // eslint-disable-next-line no-console + console.log('[NobleBLE] NOBLE bootstrap'); + } @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + }Also applies to: 1398-1400
1448-1449
: Log byte length, not hex charshexData.length counts nibbles.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
1075-1090
: Broaden isPairingError input and use normalized messagePrevents crashes when callbacks pass strings.
-function isPairingError(error: Error): boolean { +function isPairingError(error: unknown): boolean { const keywords = [ 'authentication', 'pairing', 'bonding', 'insufficient', 'security', 'authorization', 'permission', 'access denied', ]; - const errorMessage = error.message.toLowerCase(); + const errorMessage = toErrorMessage(error).toLowerCase(); return keywords.some(k => errorMessage.includes(k)); }Also convert write-single error paths to use toErrorMessage rather than
.message
.Also applies to: 70-84
27-27
: Fix comment typo“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (19)
package.json
(4 hunks)packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(25 hunks)packages/hd-transport-electron/src/utils/blePairing.ts
(1 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)packages/shared/src/constants.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: lint (22)
🔇 Additional comments (15)
package.json (1)
68-71
: Pending: generate lockfile & audit to validate pinsI ran
npm ls sha.js cipher-base micromatch
and saw:
- No dependents for sha.js or cipher-base
- [email protected] only under dev-deps (jest, globby, etc.)
Without a lockfile,
npm audit
can’t report vulnerabilities. Please:npm install --package-lock-only npm audit --json | jq '{advisories:.advisories,metadata:.metadata}'Share the audit output—especially any advisories for sha.js, cipher-base, or micromatch. Then we can confirm that these pinned versions actually address Snyk’s “2 Critical” findings, and suggest targeted overrides or upgrades if needed.
packages/hd-transport-lowlevel/package.json (1)
3-3
: LGTM: version alignment to alpha.7The version bump is consistent with the monorepo changes.
packages/hd-transport-http/package.json (3)
3-3
: LGTM: package version to alpha.7Matches the coordinated release.
27-28
: LGTM: internal deps alignedhd-shared and hd-transport bump to alpha.7 look correct.
26-31
: All axios dependencies upgraded with zero vulnerabilitiesWe’ve bumped axios to ^1.11.0 in all three packages and re-ran audits. No high- or critical-severity issues remain. Ready to merge.
• packages/hd-transport-http/package.json – axios → ^1.11.0
• packages/hd-transport-emulator/package.json – axios → ^1.11.0
• packages/core/package.json – axios → ^1.11.0packages/hd-transport-react-native/package.json (1)
3-3
: LGTM: version bumpalpha.7 aligns with sibling packages.
packages/hd-transport-emulator/package.json (3)
3-3
: LGTM: package version to alpha.7Consistent with the release train.
27-28
: LGTM: internal deps alignedhd-shared/hd-transport moved to alpha.7 as expected.
26-31
: axios bump verified – no more vulnerabilitiesWe’ve updated axios to ^1.11.0 and reran the checks in packages/hd-transport-emulator:
- npm ls shows [email protected]
- npm audit reports zero vulnerabilities
All clear. Merging this change.
packages/hd-transport-electron/package.json (1)
3-3
: LGTM: version bumpalpha.7 matches the monorepo push.
packages/core/package.json (1)
3-4
: Good bump and clearer description.Version aligns with the monorepo alpha.7 wave, and the description reads well.
packages/hd-ble-sdk/package.json (1)
3-3
: Aligned version.Alpha.7 bump matches the rest of the stack.
packages/hd-web-sdk/package.json (1)
3-3
: Version alignment looks good.Alpha.7 keeps this in lockstep with core/shared/transports.
packages/hd-transport-web-device/package.json (1)
3-3
: Alpha.7 bump is consistent.Matches the rest of the transport layer.
packages/connect-examples/expo-example/package.json (1)
3-3
: Example version bump OK.Keeps the sample aligned with SDK alpha.7.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/hd-transport-lowlevel/package.json (1)
1-25
: Consider sideEffects and files for lean publishesMirror the pattern used in other packages to keep artifacts small and tree‑shakable.
{ "name": "@onekeyfe/hd-transport-lowlevel", "version": "1.1.10-alpha.8", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ], "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",packages/hd-transport-react-native/package.json (1)
1-27
: Add sideEffects and files to improve publish footprintSmall change, big win for consumers.
{ "name": "@onekeyfe/hd-transport-react-native", "version": "1.1.10-alpha.8", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ], "homepage": "https://github.com/OneKeyHQ/hardware-js-sdk#readme",packages/hd-ble-sdk/package.json (1)
1-9
: Add sideEffects/files for smaller publishesHelps tree-shaking and cuts npm tarball size.
{ "name": "@onekeyfe/hd-ble-sdk", "version": "1.1.10-alpha.8", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-web-device/package.json (1)
1-9
: Consider adding sideEffects/files for publish hygieneImproves tree-shaking and reduces package size.
{ "name": "@onekeyfe/hd-transport-web-device", "version": "1.1.10-alpha.8", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],
♻️ Duplicate comments (40)
packages/shared/package.json (1)
3-3
: Hygiene: add files whitelist and mark as side-effect freeSlimmer publishes and better tree-shaking.
Apply:
"name": "@onekeyfe/hd-shared", "version": "1.1.10-alpha.8", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "package.json", + "README.md", + "LICENSE" + ],packages/hd-transport-http/package.json (1)
26-31
: Blocker: axios 0.27.x carries known vulns; upgrade to secure 1.xSnyk reports 2 Critical issues. axios ^0.27.2 is a common culprit. Bump to the latest 1.x and re-run audits.
Recommended change:
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.8", "@onekeyfe/hd-transport": "1.1.10-alpha.8", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Run to confirm:
#!/bin/bash set -euo pipefail cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm audit --omit=dev || true npm view axios dist-tags --json npm ls axios --depth=0 || truepackages/hd-transport-electron/src/noble-ble-handler.ts (14)
397-405
: Targeted scan: redundant removeListener and weak error typingAvoid remove-then-add loop; normalize errors.
Apply:
- logger?.info('[NobleBLE] Starting targeted scan for device:', targetDeviceId); + logger?.info('[NobleBLE] Starting targeted scan for device:', targetDeviceId); @@ - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + noble?.on('discover', onDiscover); @@ - noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error: unknown) => { if (error) { clearTimeout(timeout); - if (noble) { - noble.removeListener('discover', onDiscover); - } + noble?.removeListener('discover', onDiscover); logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, String((error as any)?.message ?? error))); return; }Also applies to: 414-433, 436-449
661-686
: Force reconnect: avoid.message
on unknown errorsNormalize error first.
Apply:
- peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${String((error as any)?.message ?? error)}`));
947-975
: Connect path: unknown error typing and normalized messagesPrevent
.message
on non-Error; ensure disconnect on discovery failure stays.Apply:
- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, String((error as any)?.message ?? error))); return; } @@ - } catch (discoveryError) { + } catch (discoveryError: any) {
21-21
: Type imports from @stoprocent/noble may be missing; add shim or import from 'noble'Some builds of @stoprocent/noble don’t ship TS types. Import types from 'noble' or add a local module declaration re-exporting them.
Apply one:
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';Or add a d.ts:
// types/stoprocent-noble.d.ts declare module '@stoprocent/noble' { export * from 'noble'; const noble: any; export default noble; }
27-27
: Typo in comment“bundlinpissues” → “bundling issues”.
Apply:
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
100-105
: Cut noise: move notification logs to debugPer-notification info logs are noisy and can hurt perf. Use debug.
Apply:
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { @@ - logger?.info('[NobleBLE] Initialized new packet state for device:', deviceId); + safeLog(logger, 'debug', '[NobleBLE] Init packet state', { deviceId });Also applies to: 111-111
578-606
: Service/characteristic discovery: treat callback error as unknown and normalizeDon’t assume Error. Avoid
.message
on non-Error.Apply:
- ONEKEY_SERVICE_UUIDS, - (error: Error | undefined, services: Service[]) => { + ONEKEY_SERVICE_UUIDS, + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, String((error as any)?.message ?? error))); return; } @@ - [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], - (error: Error | undefined, characteristics: Characteristic[]) => { + [ONEKEY_WRITE_CHARACTERISTIC_UUID, ONEKEY_NOTIFY_CHARACTERISTIC_UUID], + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, String((error as any)?.message ?? error))); return; }Also applies to: 607-647, 649-653
732-735
: Fresh connect: same error normalizationAvoid assuming Error.
Apply:
- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${String((error as any)?.message ?? error)}`));
1108-1123
: Chunk writes: don’t log full payloads; use with-response; normalize errorsTrim logs and pass withoutResponse=false.
Apply:
- logger?.info('[NobleBLE] Splitting into chunks:', { + logger?.info('[NobleBLE] Splitting into chunks:', { @@ - logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { + const preview = chunk.toString('hex').slice(0, 32); + logger?.info(`[NobleBLE] Writing chunk ${chunkIndex}:`, { deviceId, chunkIndex, chunkSize: chunk.length, - chunkData: chunk.toString('hex'), - withResponse: true, + chunkPreview: `${preview}...`, + withoutResponse: false, }); @@ - writeCharacteristic.write(chunk, true, (error: Error | undefined) => { + writeCharacteristic.write(chunk, false, (error: unknown) => { if (error) { logger?.error(`[NobleBLE] Chunk ${chunkIndex} write failed:`, error); reject( ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, error.message) ); return; } resolve(); });Also applies to: 1125-1145
1196-1203
: Subscribe: guard while unsubscribing to avoid racesWait if an unsubscribe is in-flight before starting a new subscribe.
Apply:
- const opState = subscriptionOperations.get(deviceId); + const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { @@ + if (opState === 'unsubscribing') { + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }Also applies to: 1204-1209
1252-1264
: Rebuild subscription: handle unknown errors in callbacksNormalize subscribe/unsubscribe callback errors.
Apply:
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) return reject(new Error(String((error as any)?.message ?? error))); + resolve(); + }); + }); await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) { + reject(new Error(String((error as any)?.message ?? error))); return; } resolve(); }); });
1294-1313
: Gate early console logs behind a debug flag or switch to loggerAvoid stray console logs in production.
Apply:
- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + if (process.env.ONEKEY_BLE_DEBUG) { + // eslint-disable-next-line no-console + console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + } @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] NOBLE bootstrap'); @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + }
1361-1361
: Log bytes, not hex charshexData.length counts nibbles. Divide by 2.
Apply:
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
82-82
: Make connect timeout configurable3 seconds is too short, especially on Windows where pairing can take up to 30 seconds. Pull this value from an environment variable and default to 30000 ms to avoid spurious timeouts.
• File: packages/hd-transport-electron/src/noble-ble-handler.ts
• Line: 82Apply:
- const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection + const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 30000); // default to 30 spackages/hd-web-sdk/package.json (1)
3-3
: Publish hygiene: add files and sideEffectsKeep the package lean and tree-shakeable.
Apply:
"name": "@onekeyfe/hd-web-sdk", "version": "1.1.10-alpha.8", "author": "OneKey", + "sideEffects": false, + "files": [ + "build/**/*", + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/connect-examples/expo-playground/package.json (2)
3-3
: Confirm 1.1.10-alpha.8 is published before mergingPinned example app versions often break installs if tags aren’t on npm. Please verify alpha.8 exists for all three internal SDK packages.
Run:
#!/bin/bash set -euo pipefail ver="1.1.10-alpha.8" for p in "@onekeyfe/hd-core" "@onekeyfe/hd-shared" "@onekeyfe/hd-web-sdk"; do echo -n "$p@$ver => " npm view "$p@$ver" version --silent || echo "NOT PUBLISHED" done
20-22
: Consider workspace protocol for local dev; keep pins only if you guarantee publishIf this example is meant to run from the monorepo, use workspaces. If it must install from npm, keep pins but ensure they are published.
- "@onekeyfe/hd-core": "1.1.10-alpha.8", - "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-web-sdk": "1.1.10-alpha.8", + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-web-sdk": "workspace:*",packages/hd-transport/package.json (1)
1-31
: Add sideEffects and files to slim the publish and improve tree‑shakingThis keeps the npm tarball small and signals safe tree-shaking.
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10-alpha.8", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ], "author": "OneKey",packages/hd-transport-lowlevel/package.json (1)
22-24
: Use workspace protocol for internal deps to reduce churnExact prerelease pins are brittle across the monorepo. Workspaces simplify bumps.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport": "1.1.10-alpha.8" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }packages/hd-transport-react-native/package.json (1)
22-23
: Switch internal deps to workspaces to reduce manual bumpsKeeps RN transport in lockstep with shared/transport without editing pins each time.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport": "1.1.10-alpha.8", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" }packages/hd-transport-electron/package.json (4)
23-26
: Declare Linux HCI backend as an optional peer for better DXMost Linux users need @stoprocent/bluetooth-hci-socket. Declare it as an optional peer to guide installs without breaking macOS/Windows.
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Rebuild native modules against Electron to avoid ABI mismatch@noble’s stack may include native addons. Add a postinstall rebuild to prevent runtime load errors.
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket" },Also add the tool:
"devDependencies": { "@types/web-bluetooth": "^0.0.17", "electron": "^25.0.0", + "electron-rebuild": "^3.6.0", "typescript": "^5.3.3" }
Please run your Electron example to verify the addon loads without NODE_MODULE_VERSION errors.
28-33
: Use workspace protocol for internal packages to reduce churnThis keeps the monorepo in sync and avoids unpublished pins during development.
- "@onekeyfe/hd-core": "1.1.10-alpha.8", - "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport": "1.1.10-alpha.8", + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*",
31-31
: Allow patch updates for nobleUse a caret so you pick up critical fixes automatically.
- "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4",packages/core/package.json (2)
1-9
: Improve publish hygiene: mark side-effect-free and limit published filesThis helps tree-shaking and reduces package size.
{ "name": "@onekeyfe/hd-core", "version": "1.1.10-alpha.8", "description": "Core processes and APIs for communicating with OneKey hardware devices.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],
28-31
: Switch internal deps to workspace protocol; bump axios to a supported major
- Use workspace:* for local cohesion.
- axios 0.27.x is dated and may trigger security tools. Move to 1.x if possible.
- "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport": "1.1.10-alpha.8", - "axios": "^0.27.2", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", + "axios": "^1.7.4",Please run your tests and Snyk/audit to confirm no breaking changes or open CVEs remain.
packages/hd-ble-sdk/package.json (1)
23-26
: Adopt workspace protocol for internal depsKeeps the monorepo aligned and avoids reliance on unpublished alphas during dev.
- "@onekeyfe/hd-core": "1.1.10-alpha.8", - "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.8" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*"packages/hd-transport-web-device/package.json (2)
23-25
: Use workspace protocol for internal depsPrevents 404s when alpha tags aren’t yet published.
- "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport": "1.1.10-alpha.8" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*"
27-27
: DevDependency can also use workspaceKeeps local examples consistent.
- "@onekeyfe/hd-transport-electron": "1.1.10-alpha.8", + "@onekeyfe/hd-transport-electron": "workspace:*",packages/connect-examples/expo-example/package.json (1)
10-10
: Use a single cross-envOne cross-env is enough to set multiple vars.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",packages/hd-common-connect-sdk/package.json (3)
3-3
: Add CHANGELOG entry and tag for 1.1.10-alpha.8Version bump is fine. Please add release notes and push a matching git tag to keep bookkeeping tight.
Run:
#!/bin/bash # Check for CHANGELOG entry (repo root and package dir) rg -nP '^\s*##\s*\[?1\.1\.10-alpha\.8\]?' -C2 --glob '!**/node_modules/**' || echo "No 1.1.10-alpha.8 entry found" # List matching tags (won't show if CI doesn't have git) git tag -l | rg '^1\.1\.10-alpha\.8$' || echo "No 1.1.10-alpha.8 tag found"
23-28
: Confirm alpha.8 is published for all internal depsAvoid broken installs by ensuring every referenced alpha.8 exists on npm.
#!/bin/bash set -euo pipefail pkgs=( "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" ) for p in "${pkgs[@]}"; do printf "%-55s" "$p" npm view "$p" version >/dev/null 2>&1 && echo "published" || echo "NOT PUBLISHED" done
23-28
: Optional: switch internal deps to workspace protocolUsing workspace:* for internal packages reduces churn across the monorepo.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.8", - "@onekeyfe/hd-shared": "1.1.10-alpha.8", - "@onekeyfe/hd-transport-emulator": "1.1.10-alpha.8", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.8", - "@onekeyfe/hd-transport-lowlevel": "1.1.10-alpha.8", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.8" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*" }packages/connect-examples/electron-example/package.json (5)
25-26
: Externalize native modules and verify asarUnpackWebpack tends to bundle everything. Keep noble and the HCI socket external so native bindings load correctly, and make sure electron-builder asarUnpack includes them.
#!/bin/bash set -euo pipefail # Verify electron-builder config has proper asarUnpack entries fd --type f 'electron-builder.config.*' packages/connect-examples/electron-example | xargs -I{} sh -c 'echo ">> {}"; rg -nP "asarUnpack.*@stoprocent/(noble|bluetooth-hci-socket)" -C2 "{}" || echo "asarUnpack entries missing"' # Check webpack externals don’t allowlist these native modules cfg="packages/connect-examples/electron-example/webpack.config.ts" if [ -f "$cfg" ]; then echo ">> $cfg" rg -nP '@stoprocent/(noble|bluetooth-hci-socket)' -C3 "$cfg" || true rg -n 'externals' -n -C3 "$cfg" || true else echo "webpack.config.ts not found in expected path" fi
5-7
: Fix package description (still outdated here)Earlier comment said this was fixed, but it’s still “End-to-end encrypted workspaces for teams.” Please update to reflect the Electron hardware example.
"productName": "HardwareExample", "executableName": "onekey-hardware-example", "version": "1.1.10-alpha.8", "author": "OneKey", - "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",
18-20
: Add CI-safe build scripts that never publishCreate make:*:ci variants to avoid accidental publishes in PR builds.
"scripts": { "rebuild:deps": "electron-builder install-app-deps", - "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", - "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", + "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
25-26
: Ensure BLE native stack completeness on LinuxExplicitly depend on @stoprocent/bluetooth-hci-socket so Electron main can load the HCI native binding at runtime. Optional deps can be skipped by packagers.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.8", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4",
25-26
: Drop node-fetch v2; use global fetch (Electron/Node 18+)Removes a legacy dependency and reduces native rebuilds. Replace imports with the global fetch API (or undici if you need stricter semantics).
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.8", "@stoprocent/noble": "2.3.4", "debug": "4.3.4", "electron-is-dev": "^3.0.1", "electron-log": "^5.1.5", "electron-updater": "^6.2.1", "fs-extra": "^11.2.0", - "node-fetch": "^2.6.7" + // use global fetch provided by Node/Electron; remove node-fetch },Follow-up: replace any
import fetch from 'node-fetch'
orrequire('node-fetch')
in code.#!/bin/bash rg -nP "from 'node-fetch'|require\(['\"]node-fetch['\"]\)" -C2 packages || echo "No node-fetch imports found"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (17)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(25 hunks)packages/hd-transport-electron/src/utils/windowsBleRecovery.ts
(1 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Publish v1.1.10-alpha.8
- GitHub Check: lint (22)
- GitHub Check: build (22)
- GitHub Check: Socket Security: Pull Request Alerts
🔇 Additional comments (11)
packages/hd-web-sdk/package.json (1)
24-27
: All internal alpha.8 dependencies confirmed published
- @onekeyfe/[email protected] – published
- @onekeyfe/[email protected] – published
- @onekeyfe/[email protected] – published
- @onekeyfe/[email protected] – published
No install blockers remain. Ready to merge.
packages/hd-transport/package.json (1)
3-4
: LGTM on version and descriptionVersion aligns with the repo and the description is clear.
packages/hd-transport-lowlevel/package.json (1)
3-3
: Version bump looks goodNo issues spotted with the metadata change.
packages/hd-transport-emulator/package.json (1)
3-3
: LGTM on package versionNo concerns with the version alignment.
packages/hd-transport-react-native/package.json (1)
3-3
: Version alignment OKNo functional concerns with the bump.
packages/core/package.json (2)
7-7
: License consistency checkOther packages use MIT; this one uses ISC. Confirm the choice is intentional and aligned with org policy.
28-29
: Sanity check passed: internal packages are publishedBoth @onekeyfe/[email protected] and @onekeyfe/[email protected] are available on npm. You can safely merge.
packages/hd-ble-sdk/package.json (1)
23-26
: Alpha.8 versions published
All three internal dependencies—@onekeyfe/hd-core, @onekeyfe/hd-shared, and @onekeyfe/hd-transport-react-native—are live on npm at 1.1.10-alpha.8. Safe to merge.packages/hd-transport-web-device/package.json (1)
23-27
: All alpha.8 artifacts are published to npmThe npm view check confirms that version 1.1.10-alpha.8 exists for all four packages, so downstream installs won’t break:
- @onekeyfe/[email protected]
- @onekeyfe/[email protected]
- @onekeyfe/[email protected]
- @onekeyfe/[email protected]
No further action needed.
packages/connect-examples/expo-example/package.json (1)
22-25
: All alpha.8 packages are publishedI ran
npm view
on each of the four 1.1.10-alpha.8 packages and confirmed they’re all available on npm. The example will install cleanly for users outside the repo. You can still switch toworkspace:*
versions locally if you prefer.packages/connect-examples/electron-example/package.json (1)
5-5
: Action Required: Triage 2 Critical Snyk Issues in electron-example Before Merge
- Open the Snyk report linked in this PR. Identify the exact packages and file paths flagged as Critical.
- In packages/connect-examples/electron-example/package.json
• node-fetch@^2.6.7 is included and has known vulnerabilities (e.g. CVE-2022-0235: exposure of sensitive data; CVSS 6.1 MEDIUM (advisories.gitlab.com), CVE-2022-2596: DoS; CVSS 5.9 MEDIUM (github.com)).
• Remove or upgrade node-fetch to a safe version (>=3.2.10) or swap in undici/Electron’s built-in fetch.- Review native-packaging deps (electron, electron-builder). Upgrade or patch any flagged versions.
- For each Critical issue, apply a fix (upgrade, replace, patch) or suppress with a clear justification.
- Don’t merge until Snyk shows zero Critical findings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (19)
package.json (3)
33-35
: Make Lerna publish non-interactive in CIAdd --yes so CI doesn’t hang on prompts.
- "publish-packages": "yarn check-versions && lerna publish from-package", + "publish-packages": "yarn check-versions && lerna publish from-package --yes",
40-41
: Add CI-safe desktop build variants (no publish)Provide :ci scripts that skip publishing and point workflows to them.
"scripts": { @@ "build:example:desktop:mac": "cd ./packages/connect-examples/electron-example && yarn make:mac", "build:example:desktop:win": "cd ./packages/connect-examples/electron-example && yarn make:win", + "build:example:desktop:mac:ci": "cd ./packages/connect-examples/electron-example && yarn make:mac:ci", + "build:example:desktop:win:ci": "cd ./packages/connect-examples/electron-example && yarn make:win:ci",
86-86
: Scope electron-builder to the example appKeep electron-builder in packages/connect-examples/electron-example/devDependencies to avoid heavy installs at the root. Update root scripts to call the workspace script.
packages/hd-transport-electron/src/noble-ble-handler.ts (16)
479-487
: Normalize scan callback error; don’t assume ErrorType as unknown and stringify safely.
- noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error: unknown) => { if (error) { clearTimeout(timeout); - if (noble) { - noble.removeListener('discover', onDiscover); - } + noble?.removeListener('discover', onDiscover); logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, String((error as any)?.message ?? error))); return; }
793-803
: Fresh peripheral connect: normalize error- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${String((error as any)?.message ?? error)}`)); } else {
21-21
: Type imports from @stoprocent/noble may failUse types from 'noble' or add a local d.ts shim. Runtime can still require '@stoprocent/noble'.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';Would you like a tiny d.ts that re-exports types if you prefer keeping @stoprocent/noble?
96-101
: Reduce notification log noiseUse debug to avoid spamming info logs per packet.
- logger?.info('[NobleBLE] Notification', { + logger?.debug('[NobleBLE] Notification', { deviceId, dataLength: data.length, });
471-475
: Redundant removeListener before addRemoving the same handler you’re about to add is unnecessary. Just attach the listener.
- // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + noble?.on('discover', onDiscover);
744-748
: Force reconnect: avoid .message on non-Error- peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${String((error as any)?.message ?? error)}`)); } else {
1010-1015
: Connect callback: error typing and message safety- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, String((error as any)?.message ?? error))); return; }
1066-1101
: Unsubscribe: error is unknown; always normalize- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
1128-1133
: Guard subscribe against in-flight unsubscribeWait until the previous unsubscribe finishes to avoid races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1178-1196
: Resubscribe helpers: normalize unsubscribe/subscribe errorsHandle callback errors as unknown and convert to Error.
- async function rebuildAppSubscription( + async function rebuildAppSubscription( deviceId: string, notifyCharacteristic: Characteristic ): Promise<void> { // 监听器已在上面清理,这里不需要重复清理 - await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) return reject(new Error(String((error as any)?.message ?? error))); + resolve(); + }), + ); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(String((error as any)?.message ?? error))); + resolve(); + }); + });
1221-1234
: Gate console logs behind a debug flagAvoid unconditional console noise in production.
- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + } @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log('[NobleBLE] NOBLE bootstrap'); + } @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log('[NobleBLE] electron-log loaded.'); + } @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log('[NobleBLE] electron.ipcMain loaded.'); + }
1241-1242
: Gate handler registration console log- console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + }
1290-1294
: Log byte length, not hex charshexData.length counts nibbles. Log bytes.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
654-661
: Characteristic discovery: same normalization- (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, String((error as any)?.message ?? error))); return; }
82-82
: 3s connection timeout may be too short; make it configurableWindows pairing can need longer. Gate by env with a safe default.
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 3000);
635-641
: Service discovery: error typing and message safetyTreat error as unknown and normalize message.
- (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, String((error as any)?.message ?? error))); return; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lock
is excluded by!**/yarn.lock
,!**/*.lock
📒 Files selected for processing (3)
package.json
(4 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(24 hunks)packages/hd-transport-electron/src/utils/write.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: lint (22)
- GitHub Check: Socket Security: Pull Request Alerts
- GitHub Check: build (22)
🔇 Additional comments (3)
package.json (3)
33-33
: Good preflight check gateNice addition. Running check-versions before publish reduces release mistakes.
54-54
: Dev script looks goodScoped dev:transport-electron runner is clear and consistent with others.
68-70
: Verify lockfile overrides
- Root package.json’s resolutions pin
• micromatch→4.0.8
• sha.js→2.4.12
• cipher-base→1.0.5- yarn.lock shows those versions locked.
- No workspace package declares these deps directly—no conflicts.
Please run Snyk and your full test suite to confirm these pins clear the 2 critical vulnerabilities and don’t break any transitive dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (18)
packages/hd-transport-electron/src/utils/write.ts (5)
121-134
: Fix timeout: clear timer to avoid unhandled rejectionPromise.race leaves the timeout pending; when it fires it can reject unhandled. Clear it on settle.
-async function writeWithTimeout( - doWrite: () => Promise<void>, - timeoutMs: number = RETRY_CONFIG.WRITE_TIMEOUT -): Promise<void> { - const timeoutPromise = new Promise<never>((_, reject) => { - setTimeout(() => { - reject( - ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, `Write timeout after ${timeoutMs}ms`) - ); - }, timeoutMs); - }); - - return Promise.race([doWrite(), timeoutPromise]); -} +async function writeWithTimeout( + doWrite: () => Promise<void>, + timeoutMs: number = RETRY_CONFIG.WRITE_TIMEOUT +): Promise<void> { + return new Promise<void>((resolve, reject) => { + const timer = setTimeout(() => { + reject(ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, `Write timeout after ${timeoutMs}ms`)); + }, timeoutMs); + doWrite() + .then(() => resolve()) + .catch(reject) + .finally(() => clearTimeout(timer)); + }); +}
257-265
: Windows retry: log says “wait” but code doesn’t waitInsert the 3s backoff between attempts per the PR intent (10 × 3s = 30s).
logger?.info('[BLE-Write] Waiting before retry', { deviceId, attempt, context: contextLabel, }); - // 继续下一次重试 + // 继续下一次重试 + await wait(3000);
280-295
: Noble write flag inverted (withoutResponse vs withResponse)Use withoutResponse=true only for chunked writes. Single packets should expect a response.
- const doWrite = (): Promise<void> => - new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + const doWrite = (): Promise<void> => + new Promise((resolve, reject) => { + // Chunked writes: withoutResponse=true; single: false + const withoutResponse = options?.chunkIndex !== undefined; + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { const errorMessage = options?.chunkIndex !== undefined ? `Chunk ${options.chunkIndex} write failed: ${error}` : `Single packet write failed: ${error}`; logger?.error(`[NobleBLE] ${errorMessage}`); reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, String(error))); return; } resolve(); }); });
299-307
: Windows (paired) path should honor timeoutEven paired devices can stall. Wrap direct writes with the same timeout helper.
- return doWrite(); + return writeWithTimeout(doWrite, options?.timeoutMs ?? RETRY_CONFIG.WRITE_TIMEOUT);
320-322
: Non-Windows path should honor timeoutKeep behavior consistent across platforms.
- // 其他平台: 直接写入 - return doWrite(); + // 其他平台: 带超时的直接写入 + return writeWithTimeout(doWrite, options?.timeoutMs ?? RETRY_CONFIG.WRITE_TIMEOUT);packages/hd-transport-electron/src/noble-ble-handler.ts (13)
21-21
: Type imports from @stoprocent/noble may be missingPrefer importing types from 'noble' or add a local d.ts shim.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';
32-32
: Fix typo in comment“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
102-107
: Reduce notification log noiseUse debug level for per-notification logs.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, });
478-482
: Targeted scan: remove redundant removeListener; normalize error typingAvoid unnecessary removeListener before add; handle error as unknown and normalize.
- // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + // Attach discover listener for target device + noble?.on('discover', onDiscover); @@ - if (noble) { - noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + if (noble) { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error: unknown) => { if (error) { clearTimeout(timeout); - if (noble) { - noble.removeListener('discover', onDiscover); - } + noble?.removeListener('discover', onDiscover); logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error))); return; }Also add the import:
-import { safeLog } from './types/noble-extended'; +import { safeLog, toErrorMessage } from './types/noble-extended';Also applies to: 486-499
642-648
: Service/characteristic discovery: error should be unknown; normalize messagePrevents crashes on string errors.
- (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; } @@ - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }Also applies to: 664-668
752-755
: Force reconnect: normalize error before constructing messageAvoids .message on non-Error.
- logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + logger?.error('[NobleBLE] Force reconnect failed:', error); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));
801-804
: Fresh peripheral connect: normalize errorSame issue.
- if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + if (error) { + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`));
1017-1022
: Connect callback: type unknown; normalize errorSafer error handling.
- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; }
1073-1107
: Unsubscribe: error is unknown; always normalizeKeep logs/messages consistent.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', toErrorMessage(error)); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
1184-1203
: Rebuild subscription: treat callback errors as unknown; normalizeAvoid unsafe .message access.
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + });
1135-1141
: Guard subscribe against in-flight unsubscribeWait until op state returns to idle to avoid races.
const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + // eslint-disable-next-line no-await-in-loop + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1233-1247
: Gate console logs behind debug; prefer loggerReduce noise in production.
- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] NOBLE bootstrap'); @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded successfully.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded successfully.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + }Also applies to: 1286-1288, 1360-1361
1336-1342
: IPC write log: report bytes, not hex charshexData.length is nibbles. Divide by 2.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/hd-transport-electron/src/noble-ble-handler.ts
(25 hunks)packages/hd-transport-electron/src/utils/write.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (2)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
991-993
: Small wait before discovery: good callThe 300ms settle helps avoid flaky discovery after immediate connects.
589-603
: Listener cleanup utility: solidClears Noble listeners defensively and logs errors. This reduces leak risk.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
♻️ Duplicate comments (4)
packages/hd-transport-electron/src/utils/write.ts (4)
103-116
: Fix timeout helper to clear timer (regression).Promise.race leaves a dangling timer that can later reject. Clear it on settle.
-async function writeWithTimeout( - doWrite: () => Promise<void>, - timeoutMs: number = RETRY_CONFIG.WRITE_TIMEOUT -): Promise<void> { - const timeoutPromise = new Promise<never>((_, reject) => { - setTimeout(() => { - reject( - ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, `Write timeout after ${timeoutMs}ms`) - ); - }, timeoutMs); - }); - - return Promise.race([doWrite(), timeoutPromise]); -} +async function writeWithTimeout( + doWrite: () => Promise<void>, + timeoutMs: number = RETRY_CONFIG.WRITE_TIMEOUT +): Promise<void> { + return new Promise<void>((resolve, reject) => { + const timer = setTimeout(() => { + reject(ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, `Write timeout after ${timeoutMs}ms`)); + }, timeoutMs); + doWrite() + .then(resolve) + .catch(reject) + .finally(() => clearTimeout(timer)); + }); +}
131-212
: Add 3s wait between Windows attempts.You retry but never pause. Insert the 3s delay to match the pairing strategy.
- } + } + // Wait 3s between attempts (Windows pairing) + if (attempt < maxAttempts) { + logger?.debug('[BLE-Write] Waiting before retry', { deviceId, attempt, maxAttempts, context: contextLabel }); + await wait(3000); + }
225-240
: Invert Noble write flag: use withoutResponse (not withResponse).Noble API is write(data, withoutResponse, cb). Single packet should request a response; chunks can skip it.
- const doWrite = (): Promise<void> => - new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + const doWrite = (): Promise<void> => + new Promise((resolve, reject) => { + const withoutResponse = options?.chunkIndex !== undefined; // chunks: no response; single: with response + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => {
265-267
: Honor timeout on non-Windows too.Keep behavior consistent across platforms.
- // 其他平台: 直接写入 - return doWrite(); + // 其他平台: 带超时的直接写入 + return writeWithTimeout(doWrite, options?.timeoutMs);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/hd-transport-electron/src/utils/write.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (2)
packages/hd-transport-electron/src/utils/write.ts (2)
139-142
: Use a disconnection-specific error code (not BleConnectedError).These branches represent disconnects or failed rebuilds, not “connected” errors. Switch to the appropriate code from hd-shared.
Would you confirm the correct constant (e.g., BleDisconnected/BleDeviceNotConnected) in @onekeyfe/hd-shared and replace here?
Also applies to: 203-206
39-76
: Good DI for connection/pairing/rebuild hooks.Clear separation and injectability make this easy to test and evolve.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
♻️ Duplicate comments (24)
packages/connect-examples/expo-playground/package.json (1)
20-22
: Avoid brittle exact pins in example app.Use workspace protocol (for local dev) or a caret range. Exact pins break if 1.1.10 isn’t on npm yet.
Apply one of:
- "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-web-sdk": "1.1.10", + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-web-sdk": "workspace:^",or:
- "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-web-sdk": "1.1.10", + "@onekeyfe/hd-core": "^1.1.10", + "@onekeyfe/hd-shared": "^1.1.10", + "@onekeyfe/hd-web-sdk": "^1.1.10",packages/shared/package.json (1)
3-3
: Add sideEffects and files whitelist for lean, tree‑shakable publishes.Keeps the npm tarball small and helps bundlers.
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport/package.json (1)
3-4
: Publish hygiene: add sideEffects and files.Match other packages to enable tree‑shaking and smaller publishes.
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-emulator/package.json (1)
27-31
: Upgrade axios to latest 1.x in all packagesWe still have axios pinned at ^0.27.2 in three packages. Versions in the 0.27.x line carry SSRF/CSRF vulnerabilities. Please bump them to at least 1.8.2 (ideally the latest 1.x, e.g. ^1.11.0).
Files to update:
- packages/hd-transport-http/package.json
- packages/hd-transport-emulator/package.json
- packages/core/package.json
Suggested diffs:
--- packages/hd-transport-http/package.json @@ dependencies - "axios": "^0.27.2", + "axios": "^1.11.0",--- packages/hd-transport-emulator/package.json @@ dependencies - "axios": "^0.27.2", + "axios": "^1.11.0",--- packages/core/package.json @@ dependencies - "axios": "^0.27.2", + "axios": "^1.11.0",packages/hd-transport-react-native/package.json (1)
22-23
: Use workspace protocol for internal deps (or ensure 1.1.10 is published).Reduces churn on future bumps and avoids broken installs.
- "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*",packages/hd-ble-sdk/package.json (1)
23-25
: Verify internal pins are published or switch to workspaces.Pins to 1.1.10 look fine if published. Otherwise use the workspace protocol to avoid broken installs.
Run to verify publish status:
#!/bin/bash set -euo pipefail for p in \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" do printf "%-45s" "$p" npm view "$p" version >/dev/null 2>&1 && echo "published" || echo "NOT PUBLISHED" doneOptional workspace switch:
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-react-native": "1.1.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }packages/core/package.json (1)
3-5
: Add files/sideEffects and track release (CHANGELOG + tag).Small polish that helps consumers find changes and trim bundles.
"version": "1.1.10", "description": "Core processes and APIs for communicating with OneKey hardware devices.", + "sideEffects": false, + "files": ["dist/**/*","README.md","LICENSE","package.json"],Check release artifacts:
#!/bin/bash rg -nP '^\s*##\s*\[?1\.1\.10\]?' -C2 --glob 'CHANGELOG*' --glob '!**/node_modules/**' || echo "No CHANGELOG entry found" git fetch --tags >/dev/null 2>&1 git tag -l | rg '^v?1\.1\.10$' || echo "No git tag named 1.1.10"packages/hd-transport-electron/src/utils/write.ts (6)
210-225
: Fix Noble write flag: pass withoutResponse, not withResponse.Current code always uses withoutResponse=true, which hides link errors for single packets.
- const doWrite = (): Promise<void> => - new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error: Error | undefined) => { + const doWrite = (): Promise<void> => + new Promise((resolve, reject) => { + // single packet => with response (withoutResponse=false) + // chunked packet => withoutResponse=true + const withoutResponse = options?.chunkIndex !== undefined; + writeCharacteristic.write(buffer, withoutResponse, (error: unknown) => { if (error) { const errorMessage = options?.chunkIndex !== undefined ? `Chunk ${options.chunkIndex} write failed: ${error}` : `Single packet write failed: ${error}`; logger?.error(`[NobleBLE] ${errorMessage}`); reject(ERRORS.TypedError(HardwareErrorCode.BleWriteCharacteristicError, String(error))); return; } resolve(); }); });
249-251
: Apply timeout on non‑Windows path.Parity with Windows and safer writes.
- // 其他平台: 直接写入 - return doWrite(); + // 其他平台: 带超时的直接写入 + return writeWithTimeout(doWrite, options?.timeoutMs);
229-237
: Honor timeout for Windows “paired” fast path.Avoid hanging writes when already paired.
- return doWrite(); + return writeWithTimeout(doWrite, options?.timeoutMs);
276-281
: Avoid leaking payload in info logs.Gate firstBytes behind debug or mask in production.
- logger?.info('[NobleBLE] Writing data:', { + logger?.debug?.('[NobleBLE] Writing data:', { deviceId, dataLength: buffer.length, firstBytes: buffer.subarray(0, 8).toString('hex'), });
301-301
: Normalize logger meta shape.Pass an object for consistency.
- logger?.info('[NobleBLE] Splitting into chunks:', chunks.length); + logger?.info('[NobleBLE] Splitting into chunks:', { count: chunks.length });
100-112
: Reintroduce a safe timeout helper.Used above. Clears timers to avoid unhandled rejections.
// 取消定时器式的“超时”写入窗口,改为在循环中按固定间隔发送写入 +async function writeWithTimeout( + doWrite: () => Promise<void>, + timeoutMs: number = RETRY_CONFIG.WRITE_TIMEOUT +): Promise<void> { + return new Promise<void>((resolve, reject) => { + const timer = setTimeout(() => { + reject(ERRORS.TypedError(HardwareErrorCode.BleTimeoutError, `Write timeout after ${timeoutMs}ms`)); + }, timeoutMs); + doWrite() + .then(resolve) + .catch(reject) + .finally(() => clearTimeout(timer)); + }); +}packages/hd-web-sdk/package.json (2)
24-27
: Confirm 1.1.10 artifacts are on npm or switch to workspaces.Avoids broken installs for web consumers.
#!/bin/bash set -euo pipefail for p in \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" do printf "%-45s" "$p" npm view "$p" version >/dev/null 2>&1 && echo "published" || echo "NOT PUBLISHED" doneWorkspace option:
"dependencies": { "@onekeyfe/cross-inpage-provider-core": "^0.0.17", - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-http": "1.1.10", - "@onekeyfe/hd-transport-web-device": "1.1.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*" }
3-3
: Add files/sideEffects for slimmer publish.Improves tree-shaking and package size.
"version": "1.1.10", + "sideEffects": false, + "files": [ + "build/**/*", + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-lowlevel/package.json (1)
22-24
: Workspace refs or publish verification for internal deps.Keep monorepo bumps simple and installs reliable.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }Or verify publish:
#!/bin/bash for p in "@onekeyfe/[email protected]" "@onekeyfe/[email protected]"; do printf "%-40s" "$p" npm view "$p" version >/dev/null 2>&1 && echo "published" || echo "NOT PUBLISHED" donepackages/hd-transport-http/package.json (1)
27-31
: Upgrade axios to a secure 1.x lineaxios ^0.27.2 has known vulns. Move to the latest 1.x and run tests.
"dependencies": { "@onekeyfe/hd-shared": "1.1.10", "@onekeyfe/hd-transport": "1.1.10", - "axios": "^0.27.2", + "axios": "^1.7.0", "secure-json-parse": "^4.0.0" }#!/bin/bash cd packages/hd-transport-http npm view axios dist-tags --json npm audit --omit=dev || truepackages/hd-transport-web-device/package.json (1)
23-27
: Optional: switch internal deps to workspace protocolReduces version bump churn across the monorepo.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }, "devDependencies": { - "@onekeyfe/hd-transport-electron": "1.1.10", + "@onekeyfe/hd-transport-electron": "workspace:*", "@types/w3c-web-usb": "^1.0.6", "@types/web-bluetooth": "^0.0.17" }packages/connect-examples/expo-example/package.json (1)
10-10
: Use a single cross-envOne cross-env is enough.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",packages/hd-common-connect-sdk/package.json (1)
22-28
: Optional: use workspace protocol for internal depsKeeps packages in sync locally and cuts release friction.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-emulator": "1.1.10", - "@onekeyfe/hd-transport-http": "1.1.10", - "@onekeyfe/hd-transport-lowlevel": "1.1.10", - "@onekeyfe/hd-transport-web-device": "1.1.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*" }packages/connect-examples/electron-example/package.json (4)
32-32
: Drop node-fetch v2; use global fetch (Node 18+/Electron 28).
Reduces deps and native rebuilds. Follow up by replacing imports in source.- "node-fetch": "^2.6.7" + // removed: rely on global fetch (or undici if needed)
19-20
: Add CI-safe build scripts (avoid publishing on CI).
Mirror make:* with :ci variants that pass --publish never."scripts": { @@ "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
25-26
: Add @stoprocent/bluetooth-hci-socket explicitly to avoid module-not-found on Linux.
Noble won’t always pull the HCI socket in your packaging flow. Declare it here."dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "2.2.3", "debug": "4.3.4",
Run to confirm it’s declared somewhere in the repo:
#!/bin/bash rg -n "@stoprocent/bluetooth-hci-socket" -C2
7-7
: Fix misleading package description.
This is a hardware Electron example, not “end‑to‑end encrypted workspaces”.- "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (16)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/utils/write.ts
(1 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (6)
packages/hd-transport/package.json (2)
3-3
: Version bump OK.
4-4
: Description cleanup reads well.packages/hd-transport-emulator/package.json (1)
3-3
: Version bump OK.packages/hd-transport-react-native/package.json (1)
3-3
: Version bump OK.packages/connect-examples/electron-example/package.json (2)
5-5
: Version bump looks fine.
18-18
: Good call prebuilding native deps for Electron.
This will prevent runtime rebuild churn for noble and friends.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
449-456
: Targeted scan: avoid redundant removeListener, and always detach on timeout; normalize error.return new Promise((resolve, reject) => { const timeout = setTimeout(() => { if (noble) { noble.stopScanning(); + noble.removeListener('discover', onDiscover); } logger?.info('[NobleBLE] Targeted scan timeout for device:', targetDeviceId); resolve(null); }, FAST_SCAN_TIMEOUT); @@ - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + // Attach discover listener for this scan + noble?.on('discover', onDiscover); @@ - if (noble) { - noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + if (noble) { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error: unknown) => { if (error) { clearTimeout(timeout); - if (noble) { - noble.removeListener('discover', onDiscover); - } - logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + noble?.removeListener('discover', onDiscover); + logger?.error('[NobleBLE] Failed to start targeted scan:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error))); return; } logger?.info('[NobleBLE] Targeted scan started for device:', targetDeviceId); }); }Also applies to: 478-482, 486-499
536-541
: Enumeration: normalize startScanning errors.- noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error: unknown) => { if (error) { clearTimeout(timeout); - logger?.error('[NobleBLE] Failed to start scanning:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + logger?.error('[NobleBLE] Failed to start scanning:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, toErrorMessage(error))); return; }
♻️ Duplicate comments (14)
packages/hd-transport-electron/src/noble-ble-handler.ts (14)
800-809
: Fresh peripheral connect: normalize error.- await new Promise<void>((resolve, reject) => { - freshPeripheral.connect((error: Error | undefined) => { + await new Promise<void>((resolve, reject) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${toErrorMessage(error)}`)); } else {
21-21
: Type-only imports should come from 'noble' (runtime stays @stoprocent/noble).@stoprocent/noble may not ship types. Import types from 'noble' to avoid build breaks; keep runtime require('@stoprocent/noble').
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';
32-32
: Fix typo in comment.-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
100-107
: Reduce notification log noise; use debug.- // notification telemetry - logger?.info('[NobleBLE] Notification', { + // notification telemetry + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, }); @@ - logger?.info('[NobleBLE] Initialized new packet state for device:', deviceId); + safeLog(logger, 'debug', '[NobleBLE] Initialized packet state for device:', deviceId);Also applies to: 113-114
642-648
: Service/characteristic discovery: treat callback error as unknown and normalize.- (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, toErrorMessage(error))); return; } @@ - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, toErrorMessage(error))); return; }Also applies to: 661-669
751-755
: Force reconnect: normalize error.- peripheral.connect((error: Error | undefined) => { + peripheral.connect((error: unknown) => { if (error) { logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${toErrorMessage(error)}`));
1016-1023
: Connect callback: error should be unknown; normalize for TypedError.- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, toErrorMessage(error))); return; } @@ - } catch (discoveryError) { + } catch (discoveryError) { logger?.error( '[NobleBLE] Service/characteristic discovery failed after all attempts:', discoveryError ); // Disconnect on failure connectedPeripheral.disconnect(); - reject(discoveryError); + reject(discoveryError as unknown as Error); }Also applies to: 1031-1045
1074-1108
: Unsubscribe callback: type as unknown; always normalize.- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', toErrorMessage(error)); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); }
1128-1134
: Drop hard-coded fields in subscribe context log.- logger?.info('[NobleBLE] Subscribe context', { - deviceId, - opStateBefore: subscriptionOperations.get(deviceId) || 'idle', - paired: false, - hasController: false, - }); + logger?.info('[NobleBLE] Subscribe context', { + deviceId, + opStateBefore: subscriptionOperations.get(deviceId) || 'idle', + });
1135-1140
: Guard against concurrent 'unsubscribing' state before subscribing.const opState = subscriptionOperations.get(deviceId); if (opState === 'subscribing') { // Subscription in progress; update callback and return notificationCallbacks.set(deviceId, callback); return Promise.resolve(); } + if (opState === 'unsubscribing') { + // Wait until unsubscribe completes + while (subscriptionOperations.get(deviceId) === 'unsubscribing') { + // eslint-disable-next-line no-await-in-loop + await wait(50); + } + }
1189-1203
: Rebuild subscription: normalize errors in callbacks.- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); - return; - } - resolve(); - }); - }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) return reject(new Error(toErrorMessage(error))); + resolve(); + }); + });
1233-1237
: Gate console logs behind a debug flag (or use logger.debug).- // Use console.log for initial logging as electron-log might not be available yet. - console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + } try { - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] NOBLE bootstrap'); @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) { + console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + }Also applies to: 1241-1247, 1287-1288
1336-1342
: IPC write: log bytes, not hex length.- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
24-25
: Import the error normalizer to avoid unsafe .message access.You use raw error.message in multiple callbacks. Pull in toErrorMessage now and apply below.
-import { safeLog } from './types/noble-extended'; +import { safeLog, toErrorMessage } from './types/noble-extended';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/hd-transport-electron/src/noble-ble-handler.ts
(25 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (3)
packages/hd-transport-electron/src/noble-ble-handler.ts (3)
53-55
: LGTM: pairedDevices tracking is clear and localized.
70-75
: LGTM: removed legacy Windows watchdog comments.
991-993
: Stabilization wait: good.Short settle delay reduces flakiness after reuse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
♻️ Duplicate comments (22)
packages/connect-examples/expo-playground/package.json (1)
20-22
: Publish alpha.9 or switch to workspace protocol.Exact pins will break installs if alpha.9 isn’t on npm. Either confirm publish or resolve from the monorepo.
Option A (monorepo-friendly):
- "@onekeyfe/hd-core": "1.1.10-alpha.9", - "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-web-sdk": "1.1.10-alpha.9", + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-web-sdk": "workspace:^",Option B (keep pins; verify publish first):
#!/bin/bash set -euo pipefail for p in "@onekeyfe/hd-core" "@onekeyfe/hd-shared" "@onekeyfe/hd-web-sdk"; do echo -n "$p@1.1.10-alpha.9 => " npm view "$p@1.1.10-alpha.9" version --silent || echo "NOT PUBLISHED" donepackages/shared/package.json (1)
3-3
: Version bump looks fine; add publish hygiene (repeat).Add sideEffects and a files whitelist to keep the package lean and tree‑shakable.
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10-alpha.9", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-web-sdk/package.json (1)
3-3
: Optional: add sideEffects/files.Slim publish and enable tree‑shaking.
"version": "1.1.10-alpha.9", + "sideEffects": false, + "files": [ + "build/**/*", + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport/package.json (1)
3-4
: Optional: publish hygiene (repeat).Add sideEffects and files to reduce package size and help tree‑shaking.
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10-alpha.9", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-lowlevel/package.json (2)
3-3
: Version bump OK; consider workspace protocol.Using workspace:* simplifies future lockstep bumps across internal packages.
- "version": "1.1.10-alpha.9", + "version": "1.1.10-alpha.9",And for deps below:
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-transport": "1.1.10-alpha.9" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }
22-23
: Optional: add files/sideEffects.Stay consistent with other packages and keep publishes slim.
"license": "MIT", "main": "dist/index.js", "types": "dist/index.d.ts", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-http/package.json (1)
27-31
: Upgrade axios to a secure 1.x line.0.27.x is dated; move to the latest 1.x to avoid known issues and get fixes.
Apply:
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.9", "@onekeyfe/hd-transport": "1.1.10-alpha.9", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }Verify:
#!/bin/bash cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm ls axios --depth=0 || true npm audit --omit=dev || truepackages/hd-transport-react-native/package.json (1)
22-23
: Use workspace protocol for internal deps (optional).Reduces churn during coordinated releases.
Apply:
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-transport": "1.1.10-alpha.9", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@onekeyfe/react-native-ble-utils": "^0.1.4", "react-native-ble-plx": "3.5.0" }packages/hd-transport-electron/package.json (3)
28-33
: Ensure native modules are rebuilt for Electron.Without rebuilds, native addons may fail to load under Electron >=25.
Add a postinstall (top-level scripts):
"scripts": { "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket" }Quick check:
#!/bin/bash rg -nP 'electron-rebuild|prebuild' -C1 || echo "No rebuild hooks found"
28-33
: Loosen noble to allow patch fixes.Use a caret so you get security/bug patches automatically.
Apply:
"dependencies": { "@onekeyfe/hd-core": "1.1.10-alpha.9", "@onekeyfe/hd-shared": "1.1.10-alpha.9", "@onekeyfe/hd-transport": "1.1.10-alpha.9", - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" },
28-33
: Declare Linux HCI as optional peer (guides users, avoids forced installs).Add optional peer to improve DX on Linux.
Add (top-level JSON, outside this hunk):
"peerDependencies": { "electron": ">=25.0.0", "electron-log": ">=4.0.0", "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, "peerDependenciesMeta": { "@stoprocent/bluetooth-hci-socket": { "optional": true } }packages/core/package.json (2)
28-29
: Consider workspace protocol for internal deps (optional).Cuts manual bumps across the monorepo.
Apply:
- "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-transport": "1.1.10-alpha.9", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*",
3-4
: Publish hygiene: trim tarball and aid tree-shaking (optional).Add files/sideEffects to ship only runtime artifacts.
Add (top-level JSON):
"sideEffects": false, "files": ["dist/**/*", "README.md", "LICENSE", "package.json"]Also applies to: 27-36
packages/hd-ble-sdk/package.json (2)
23-25
: Switch internal deps to workspace protocol (optional).Easier coordinated releases.
Apply:
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.9", - "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.9" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*" }
3-3
: Publish hygiene (optional).Add files/sideEffects to keep the package lean and tree-shakable.
Add (top-level JSON):
"sideEffects": false, "files": ["dist/**/*", "README.md", "LICENSE", "package.json"]Also applies to: 23-25
packages/hd-transport-web-device/package.json (2)
23-24
: Switch internal deps to workspace protocolReduces version bump churn across the monorepo.
Apply:
- "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-transport": "1.1.10-alpha.9" + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport": "workspace:^"
27-27
: Use workspace protocol for devDependency tooKeeps local builds aligned.
- "@onekeyfe/hd-transport-electron": "1.1.10-alpha.9", + "@onekeyfe/hd-transport-electron": "workspace:^",packages/connect-examples/expo-example/package.json (1)
10-10
: Collapse duplicate cross-env callsOne cross-env is enough.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",packages/hd-common-connect-sdk/package.json (1)
23-28
: Prefer workspace protocol for internal packagesThis cuts maintenance overhead for synchronized releases.
- "@onekeyfe/hd-core": "1.1.10-alpha.9", - "@onekeyfe/hd-shared": "1.1.10-alpha.9", - "@onekeyfe/hd-transport-emulator": "1.1.10-alpha.9", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.9", - "@onekeyfe/hd-transport-lowlevel": "1.1.10-alpha.9", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.9" + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport-emulator": "workspace:^", + "@onekeyfe/hd-transport-http": "workspace:^", + "@onekeyfe/hd-transport-lowlevel": "workspace:^", + "@onekeyfe/hd-transport-web-device": "workspace:^"packages/connect-examples/electron-example/package.json (3)
25-26
: Explicitly depend on @stoprocent/bluetooth-hci-socketAvoid “module not found” on Linux when noble loads native HCI.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.9", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4",
25-26
: Consider dropping node-fetch v2 (Node 18+ has fetch built-in)Simplify deps; use global fetch or undici if needed.
18-20
: Add CI-safe build scripts (no publish)Create make:*:ci variants that never publish.
"scripts": { @@ - "rebuild:deps": "electron-builder install-app-deps", - "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", - "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "rebuild:deps": "electron-builder install-app-deps", + "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", + "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Publish v1.1.10-alpha.9
- GitHub Check: build (22)
- GitHub Check: lint (22)
🔇 Additional comments (10)
packages/connect-examples/expo-playground/package.json (1)
3-3
: Version bump looks good.Alpha.9 aligns with the repo-wide update.
packages/hd-web-sdk/package.json (1)
8-9
: Verify types output pathYour package.json sets
"main": "build/onekey-js-sdk.js"
and"types": "dist/index.d.ts"
. The build in this sandbox failed, so please run locally:cd packages/hd-web-sdk yarn build ls dist/index.d.ts
and confirm that
dist/index.d.ts
is produced. If your d.ts files land underbuild/
instead, update the"types"
field to match.packages/hd-transport/package.json (1)
3-4
: Nice cleanup.Version bump and description read well.
packages/hd-transport-http/package.json (1)
3-3
: Version alignment looks good.Package and internal deps consistently bumped to 1.1.10-alpha.9.
Also applies to: 27-28
packages/hd-transport-react-native/package.json (1)
3-3
: Bump looks consistent.Version and internal deps aligned to 1.1.10-alpha.9.
Also applies to: 22-23
packages/hd-transport-electron/package.json (1)
3-3
: Good dependency alignment with the BLE refactor.Version bump and new deps match the noble switch and retry logic.
Also applies to: 28-33
packages/core/package.json (2)
3-4
: Nice: real description and version bump.Clearer metadata helps consumers.
27-36
: Upgrade axios to ^1.11.0 in packages/coreKeeping all packages on the latest secure versions helps us maintain a strong security posture.
• File: packages/core/package.json
• Section: dependencies (around lines 27–36)Apply this diff:
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.9", "@onekeyfe/hd-transport": "1.1.10-alpha.9", - "axios": "^0.27.2", + "axios": "^1.11.0", "bignumber.js": "^9.0.2", "bytebuffer": "^5.0.1",After you update, please verify the change and confirm no new issues:
cd packages/core npm install --legacy-peer-deps npm ls axios --depth=0 npm audit --omit=dev
packages/hd-ble-sdk/package.json (1)
3-3
: Versions aligned across BLE SDK and deps.All bumped to 1.1.10-alpha.9. Looks good.
Also applies to: 23-25
packages/hd-common-connect-sdk/package.json (1)
23-28
: Verified: All pinned 1.1.10-alpha.9 versions are publishedWe ran
npm view <pkg>@1.1.10-alpha.9
on each dependency listed in package.json. All six returned “published,” so there’s no risk of 404s downstream.• @onekeyfe/[email protected] – published
• @onekeyfe/[email protected] – published
• @onekeyfe/[email protected] – published
• @onekeyfe/[email protected] – published
• @onekeyfe/[email protected] – published
• @onekeyfe/[email protected] – publishedNo further action needed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/hd-transport-electron/src/noble-ble-handler.ts (2)
785-818
: Enumeration scan: callback error typing and normalization- noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); logger?.error('[NobleBLE] Failed to start scanning:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, String(error))); return; }
1426-1483
: DRY: reuse softRefreshSubscription instead of inline rebuildAppSubscriptionYou already import softRefreshSubscription. Remove the local helper and call the shared function to keep behavior consistent.
- // Helper: rebuild a clean application-layer subscription - async function rebuildAppSubscription( - deviceId: string, - notifyCharacteristic: Characteristic - ): Promise<void> { /* ... */ } - - await rebuildAppSubscription(deviceId, notifyCharacteristic); - subscribedDevices.set(deviceId, true); - subscriptionOperations.set(deviceId, 'idle'); + await softRefreshSubscription({ + deviceId, + notifyCharacteristic, + subscriptionOperations, + subscribedDevices, + pairedDevices, + notificationCallbacks, + processNotificationData, + logger, + });
♻️ Duplicate comments (15)
packages/hd-transport-electron/src/noble-ble-handler.ts (15)
83-83
: Make connection timeout configurable (default stays 3s)Expose via env so ops can tune relative to 30s Windows pairing window.
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 3000);Please confirm 3s is acceptable with the 30s pairing probe.
695-748
: Targeted scan: avoid redundant removeListener; normalize startScanning error- // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + // Attach discover listener for the target device + noble?.on('discover', onDiscover); @@ - if (noble) { - noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + if (noble) { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); - if (noble) { - noble.removeListener('discover', onDiscover); - } + noble?.removeListener('discover', onDiscover); logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, String(error))); return; }
1266-1299
: Connect callback: error typing and normalization- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { clearTimeout(timeout); if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, String(error))); return; }
21-21
: Types from @stoprocent/noble may not existImport types from 'noble' (or your local shim) while keeping runtime require('@stoprocent/noble'). This avoids broken type resolution.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';
27-27
: Typo: “bundlinpissues” → “bundling issues”-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
106-111
: Reduce notification log noise to debugPer-packet info logs are noisy and can hurt perf.
- logger?.info('[NobleBLE] Notification', { + safeLog(logger, 'debug', '[NobleBLE] Notification', { deviceId, dataLength: data.length, });
838-851
: Simplify Noble listener teardown and guard API-function cleanupNobleListeners(): void { - if (!noble) return; - - // 移除所有监听器以防止内存泄漏 - // Noble 使用 EventEmitter,需要使用 removeAllListeners - try { - (noble as any).removeAllListeners('discover'); - (noble as any).removeAllListeners('stateChange'); - logger?.info('[NobleBLE] All Noble listeners cleaned up'); - } catch (error) { - logger?.error('[NobleBLE] Failed to clean up some listeners:', error); - } -} +function cleanupNobleListeners(): void { + if (!noble) return; + try { + const ee = noble as unknown as { removeAllListeners?: (event?: string) => void }; + ee.removeAllListeners?.(); + logger?.info('[NobleBLE] All Noble listeners cleaned up'); + } catch (error) { + logger?.error('[NobleBLE] Failed to clean up some listeners:', error); + } +}
891-966
: Service/characteristic discovery: treat callback error as unknown and normalize- (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, String(error))); return; } @@ - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, String(error))); return; }
999-1004
: Force reconnect: avoid unsafe .message- logger?.error('[NobleBLE] Force reconnect failed:', error); - reject(new Error(`Force reconnect failed: ${error.message}`)); + logger?.error('[NobleBLE] Force reconnect failed:', error); + reject(new Error(`Force reconnect failed: ${String(error)}`));
1059-1062
: Fresh peripheral connect: avoid unsafe .message- if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + if (error) { + reject(new Error(`Fresh peripheral connection failed: ${String(error)}`));
1330-1364
: Unsubscribe: error as unknown; always reset state- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { - logger?.error('[NobleBLE] Notification unsubscription failed:', error); + logger?.error('[NobleBLE] Notification unsubscription failed:', error); } else { logger?.info('[NobleBLE] Notification unsubscription successful'); } // Remove all listeners and clear subscription status notifyCharacteristic.removeAllListeners('data'); notificationCallbacks.delete(deviceId); devicePacketStates.delete(deviceId); subscribedDevices.delete(deviceId); // 🔒 Clear operation state subscriptionOperations.set(deviceId, 'idle'); resolve(); }); });
1384-1389
: Drop hard-coded fields in subscribe context log- logger?.info('[NobleBLE] Subscribe context', { - deviceId, - opStateBefore: subscriptionOperations.get(deviceId) || 'idle', - paired: false, - hasController: false, - }); + logger?.info('[NobleBLE] Subscribe context', { + deviceId, + opStateBefore: subscriptionOperations.get(deviceId) || 'idle', + });
1489-1508
: Replace console logs with gated or logger-based output- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] NOBLE bootstrap'); @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
1556-1558
: IPC write log: report bytes, not hex chars- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
374-440
: Implement best-effort cancel when cancelOperations=trueCurrently a no-op for in-flight notifications. Unsubscribe and clear listeners before deleting maps.
// 1. 清理设备状态 - if (cleanupConnection) { + if (cleanupConnection) { + if (cancelOperations) { + const ch = deviceCharacteristics.get(deviceId); + try { + ch?.notify.removeAllListeners('data'); + ch?.notify.unsubscribe?.(() => {}); + } catch (e) { + safeLog(logger, 'warn', '[NobleBLE] Best-effort unsubscribe failed during cleanup', { deviceId, error: String(e) }); + } + } connectedDevices.delete(deviceId); deviceCharacteristics.delete(deviceId); notificationCallbacks.delete(deviceId); devicePacketStates.delete(deviceId); subscribedDevices.delete(deviceId); subscriptionOperations.delete(deviceId); pairedDevices.delete(deviceId); // 清理windows配对状态 }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/hd-transport-electron/src/ble-ops.ts
(1 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(28 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build (22)
- GitHub Check: lint (22)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (22)
packages/shared/package.json (1)
3-3
: Publish hygiene: add sideEffects/files (repeat).
Same suggestion as before to slim the package and enable tree‑shaking.Apply:
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10-alpha.10", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-web-sdk/package.json (1)
3-3
: Optional: add sideEffects/files for slimmer publishes (repeat).
Matches prior guidance; improves tree‑shaking."version": "1.1.10-alpha.10", + "sideEffects": false, + "files": [ + "build/**/*", + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport/package.json (1)
3-4
: Optional: add sideEffects/files for lean publishes (repeat).
Same nitpick as before across packages.{ "name": "@onekeyfe/hd-transport", "version": "1.1.10-alpha.10", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-emulator/package.json (1)
29-29
: Upgrade axios to 1.x across the repoAxios 0.27.x has known advisories. Bump to the latest 1.x, then regenerate your lockfile, audit, and test.
Affected files:
- packages/hd-transport-emulator/package.json (line 29)
- packages/hd-transport-http/package.json (line 29)
- packages/core/package.json (line 30)
- "axios": "^0.27.2", + "axios": "^1.11.0",After bump:
- Run
npm i --package-lock-only
to create/update your lockfile.- Run
npm audit --omit=dev
to verify no new advisories.- Run the full test suite to confirm nothing breaks.
packages/hd-transport-http/package.json (1)
27-31
: Upgrade axios to 1.x (0.27.2 has known vulns).Bump axios to a patched 1.x release and run tests/audit.
"dependencies": { "@onekeyfe/hd-shared": "1.1.10-alpha.10", "@onekeyfe/hd-transport": "1.1.10-alpha.10", - "axios": "^0.27.2", + "axios": "^1.11.0", "secure-json-parse": "^4.0.0" }#!/bin/bash cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm audit --omit=dev || true npm ls axios --depth=0 || truepackages/core/package.json (1)
28-29
: Ensure alpha.10 internal deps are published (or use workspaces).If not published, installs break. Prefer workspace protocol in monorepo.
#!/bin/bash for p in "@onekeyfe/[email protected]" "@onekeyfe/[email protected]"; do echo -n "$p => "; npm view "$p" version >/dev/null 2>&1 && echo "published" || echo "NOT PUBLISHED" donepackages/hd-transport-lowlevel/package.json (2)
22-24
: Switch internal deps to workspace protocol.Simplifies future bumps and avoids unpublished-version breaks.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.10", - "@onekeyfe/hd-transport": "1.1.10-alpha.10" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }
3-4
: Add publish hygiene (sideEffects/files).Helps tree-shaking and smaller publishes.
{ "name": "@onekeyfe/hd-transport-lowlevel", "version": "1.1.10-alpha.10", + "sideEffects": false, + "files": ["dist/**/*","README.md","LICENSE","package.json"],packages/hd-transport-react-native/package.json (1)
22-23
: Use workspace protocol for internal deps.Reduces release friction across the monorepo.
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10-alpha.10", - "@onekeyfe/hd-transport": "1.1.10-alpha.10", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@onekeyfe/react-native-ble-utils": "^0.1.4",packages/hd-transport-electron/package.json (4)
23-26
: Declare Linux HCI socket as optional peer.Improves DX on Linux without forcing installs elsewhere.
Add under peerDependencies (not a diff to the changed block):
{
"@stoprocent/bluetooth-hci-socket": ">=2.2.0"
}
and:
{
"peerDependenciesMeta": {
"@stoprocent/bluetooth-hci-socket": { "optional": true }
}
}
16-21
: Ensure native rebuild for Electron.@noble/HCI may need ABI rebuilds for Electron >=25.
#!/bin/bash # Check for electron-rebuild integration rg -nP 'electron-rebuild|prebuild' -C2 packages/hd-transport-electron || true echo 'Suggestion: add postinstall "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket"'
28-33
: Loosen noble; pick up patch fixes.Allow patched releases from @stoprocent/noble.
"dependencies": { "@onekeyfe/hd-core": "1.1.10-alpha.10", "@onekeyfe/hd-shared": "1.1.10-alpha.10", "@onekeyfe/hd-transport": "1.1.10-alpha.10", - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" },
28-33
: Prefer workspaces for internal deps.Keeps versions in lockstep without manual bumps.
"dependencies": { - "@onekeyfe/hd-core": "1.1.10-alpha.10", - "@onekeyfe/hd-shared": "1.1.10-alpha.10", - "@onekeyfe/hd-transport": "1.1.10-alpha.10", + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" },packages/hd-ble-sdk/package.json (2)
3-3
: Add publish hygiene: files + sideEffectsSmaller tarballs and better tree‑shaking.
"version": "1.1.10-alpha.10", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],
23-25
: Switch internal deps to workspace protocolReduces churn across the monorepo.
- "@onekeyfe/hd-core": "1.1.10-alpha.10", - "@onekeyfe/hd-shared": "1.1.10-alpha.10", - "@onekeyfe/hd-transport-react-native": "1.1.10-alpha.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*"Run to confirm pins are actually published before release:
#!/bin/bash set -euo pipefail for p in \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" do printf "%-55s" "$p"; npm view "$p" version >/dev/null && echo "published" || echo "NOT PUBLISHED" donepackages/hd-transport-web-device/package.json (2)
3-3
: Release hygiene: add files/sideEffects and update CHANGELOG/tagKeep artifacts slim and releases auditable.
"version": "1.1.10-alpha.10", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],Confirm a CHANGELOG entry and annotated tag exist for 1.1.10-alpha.10.
23-24
: Use workspace protocol for internal depsPrevents repeated manual bumps.
- "@onekeyfe/hd-shared": "1.1.10-alpha.10", - "@onekeyfe/hd-transport": "1.1.10-alpha.10" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*"- "@onekeyfe/hd-transport-electron": "1.1.10-alpha.10", + "@onekeyfe/hd-transport-electron": "workspace:*",Check that these versions are on npm if you keep exact pins:
#!/bin/bash set -euo pipefail for p in \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" do printf "%-55s" "$p"; npm view "$p" version >/dev/null && echo "published" || echo "NOT PUBLISHED" doneAlso applies to: 27-27
packages/hd-common-connect-sdk/package.json (2)
3-3
: Add files/sideEffects; document/tag the releaseImproves package size and traceability.
"version": "1.1.10-alpha.10", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],Ensure there’s a CHANGELOG entry and tag for 1.1.10-alpha.10.
23-28
: Prefer workspace: for internal deps*Keeps workspace packages in sync.
- "@onekeyfe/hd-core": "1.1.10-alpha.10", - "@onekeyfe/hd-shared": "1.1.10-alpha.10", - "@onekeyfe/hd-transport-emulator": "1.1.10-alpha.10", - "@onekeyfe/hd-transport-http": "1.1.10-alpha.10", - "@onekeyfe/hd-transport-lowlevel": "1.1.10-alpha.10", - "@onekeyfe/hd-transport-web-device": "1.1.10-alpha.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*"Sanity-check pins if not switching to workspace:
#!/bin/bash set -euo pipefail for p in \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" \ "@onekeyfe/[email protected]" do printf "%-60s" "$p"; npm view "$p" version >/dev/null && echo "published" || echo "NOT PUBLISHED" donepackages/connect-examples/expo-example/package.json (1)
10-10
: Use a single cross-envSimpler and clearer.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",packages/connect-examples/electron-example/package.json (2)
18-20
: Add :ci variants that never publishAvoid accidental publishes from CI.
"scripts": { @@ "rebuild:deps": "electron-builder install-app-deps", "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",
25-26
: Ensure HCI socket is installed; drop node-fetch on Electron 28+Noble needs the HCI native binding on Linux. Electron 28/Node 18 has fetch built-in.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10-alpha.10", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3", "debug": "4.3.4", @@ - "node-fetch": "^2.6.7" + // remove: Electron 28/Node 18 includes global fetchDouble-check bundling keeps these native modules external and unpacked at runtime (webpack externals + electron-builder asarUnpack).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (15)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: release-web
🔇 Additional comments (9)
packages/shared/package.json (1)
3-3
: Version bump aligned. LGTM.
Consistent with the monorepo updates.packages/hd-web-sdk/package.json (1)
24-27
: Internal deps verified – all publishedI ran the npm view check and confirmed that each internal package at version 1.1.10-alpha.10 is available:
- @onekeyfe/hd-core ⇒ 1.1.10-alpha.10
- @onekeyfe/hd-shared ⇒ 1.1.10-alpha.10
- @onekeyfe/hd-transport-http ⇒ 1.1.10-alpha.10
- @onekeyfe/hd-transport-web-device ⇒ 1.1.10-alpha.10
No broken installs expected.
packages/connect-examples/expo-playground/package.json (2)
3-3
: Version bump OK.
Private example; no publish impact.
20-22
: Deps exist on npm
Verified that versions 1.1.10-alpha.10 of
- @onekeyfe/hd-core
- @onekeyfe/hd-shared
- @onekeyfe/hd-web-sdk
are all published on npm. Lines 20–22 inpackages/connect-examples/expo-playground/package.json
are good to go.packages/hd-transport/package.json (1)
4-4
: Nice description cleanup.
Clear and user-facing.packages/hd-transport-emulator/package.json (2)
3-3
: Version bump aligned.
Looks consistent with the repo.
27-28
: Internal deps published and ready
Both @onekeyfe/[email protected] and @onekeyfe/[email protected] are live on npm. No install breakage expected.packages/core/package.json (1)
28-31
: Upgrade axios to 1.x
axios 0.27.2 has known vulnerabilities. Bump to the latest 1.x release for security and consistency.In packages/core/package.json:
- "axios": "^0.27.2", + "axios": "^1.11.0",After updating, run your install at the repo root (
npm install
oryarn install
) to refresh the lockfile. Then:
- Run tests to catch any breaking changes.
- Re-run
npm audit
to confirm the vulnerabilities are resolved.packages/connect-examples/expo-example/package.json (1)
22-25
: Example pins verified as published
All four @onekeyfe packages at version 1.1.10-alpha.10 are available on npm. Examples will install cleanly outside the monorepo.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (4)
packages/hd-transport-electron/src/ble-ops.ts (4)
1-2
: Fix type import; keep typings local and consistent@stoprocent/noble may not ship TS types. Also you mix type sources. Import both Characteristic and Logger from the local wrapper.
-import type { Characteristic } from '@stoprocent/noble'; -import type { Logger } from './types/noble-extended'; +import type { Characteristic, Logger } from './types/noble-extended';Run to confirm local typings exist and to find remaining imports from @stoprocent/noble:
#!/bin/bash fd noble-extended rg -nP "export\s+type\s+Characteristic\b" -- types/noble-extended.* || true rg -n "@stoprocent/noble" -g '!**/dist/**'
56-72
: Harden data path: structured logs + protect app callbackAvoid crashing the pipeline if the app callback throws. Use compact, structured logs.
notifyCharacteristic.removeAllListeners('data'); notifyCharacteristic.on('data', (data: Buffer) => { @@ - const result = processNotificationData(deviceId, data); - if (result.error) { - logger?.error('[BLE-OPS] Packet processing error:', result.error); - return; - } - if (result.isComplete && result.completePacket) { - const appCb = notificationCallbacks.get(deviceId); - if (appCb) appCb(result.completePacket); - } + const result = processNotificationData(deviceId, data); + if (result.error) { + logger?.error?.('[BLE-OPS] Packet processing error', { deviceId, error: result.error }); + return; + } + if (result.isComplete && result.completePacket) { + const appCb = notificationCallbacks.get(deviceId); + if (appCb) { + try { appCb(result.completePacket); } + catch (e) { logger?.error?.('[BLE-OPS] App callback failed', { deviceId, error: String(e) }); } + } + }
46-54
: Subscribe: loosen error type and normalize rejectionThe callback may pass string/Error/unknown. Use unknown and keep rejection.
- await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { if (error) { reject(error); return; } resolve(); }); });
42-45
: Unsubscribe: capture error and don’t fail hardUnsubscribe often returns spurious errors; log and continue to resubscribe.
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => resolve()); - }); + await new Promise<void>((resolve) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) logger?.warn?.('[BLE-OPS] Unsubscribe before resubscribe failed', { deviceId, error: String(error) }); + resolve(); + }); + });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/hd-transport-electron/src/ble-ops.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: lint (22)
- GitHub Check: build (22)
🔇 Additional comments (1)
packages/hd-transport-electron/src/ble-ops.ts (1)
34-36
: Keep throw: upstream handles error
softRefreshSubscription throws on missing notifyCharacteristic so the caller’s try/catch in noble-ble-handler logs the failure and retries—no UI crash.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
packages/connect-examples/electron-example/package.json (1)
24-33
: Drop node-fetch v2; use global fetch (Electron 28/Node 18+)This trims deps and native rebuilds.
"dependencies": { @@ - "node-fetch": "^2.6.7" + // use global fetch or undici if needed },Follow-up: replace imports of node-fetch with global fetch.
packages/hd-transport-electron/src/noble-ble-handler.ts (3)
810-815
: Enumerate scan callback: widen error typeTreat callback error as unknown and normalize message.
- noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); - logger?.error('[NobleBLE] Failed to start scanning:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + logger?.error('[NobleBLE] Failed to start scanning:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, (error as any)?.message ?? String(error))); return; }
1083-1090
: Fresh connection: normalize callback errorSame normalization as above.
- freshPeripheral.connect((error: Error | undefined) => { + freshPeripheral.connect((error: unknown) => { if (error) { - reject(new Error(`Fresh peripheral connection failed: ${error.message}`)); + reject(new Error(`Fresh peripheral connection failed: ${(error as any)?.message ?? String(error)}`));
1291-1303
: Connect timeout path: treat error as unknownAvoid
.message
access on non-Error.- connectedPeripheral.connect(async (error: Error | undefined) => { + connectedPeripheral.connect(async (error: unknown) => { @@ - if (error) { + if (error) { logger?.error('[NobleBLE] Connection failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleConnectedError, (error as any)?.message ?? String(error))); return; }
♻️ Duplicate comments (39)
packages/shared/package.json (1)
3-3
: Add sideEffects and a files whitelist to tighten publishes.
Keeps installs lean and enables tree-shaking.Apply:
{ "name": "@onekeyfe/hd-shared", "version": "1.1.10", "description": "Hardware SDK's shared tool library", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport/package.json (1)
3-4
: Publish hygiene: add sideEffects and files.
Align with other packages for smaller bundles.Apply:
{ "name": "@onekeyfe/hd-transport", "version": "1.1.10", "description": "Transport layer abstractions and utilities for OneKey hardware SDK.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-lowlevel/package.json (2)
22-24
: Internal deps aligned; consider workspace protocol.
Eases future version bumps in the monorepo.Apply:
"dependencies": { - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10" + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*" }
3-3
: Add sideEffects and files.
Consistent publish hygiene with other libs.Apply:
"version": "1.1.10", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-transport-electron/package.json (4)
23-26
: Declare Linux HCI socket as an optional peer.
Guides Linux users without forcing installs on macOS/Windows.Apply:
"peerDependencies": { "electron": ">=25.0.0", - "electron-log": ">=4.0.0" + "electron-log": ">=4.0.0", + "@stoprocent/bluetooth-hci-socket": ">=2.2.0" }, + "peerDependenciesMeta": { + "@stoprocent/bluetooth-hci-socket": { + "optional": true + } + },
16-21
: Electron native addon rebuild hook.
Prevent ABI mismatches at runtime.Apply:
"scripts": { "dev": "rimraf dist && rollup -c ../../build/rollup.config.js -w", "build": "rimraf dist && rollup -c ../../build/rollup.config.js", - "lint": "eslint .", - "lint:fix": "eslint . --fix" + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "postinstall": "electron-rebuild -f -w @stoprocent/noble -w @stoprocent/bluetooth-hci-socket || true" },And add the devDependency:
"devDependencies": { + "electron-rebuild": "^3.2.10", "@types/web-bluetooth": "^0.0.17", "electron": "^25.0.0", "typescript": "^5.3.3" }
28-33
: Loosen noble to pick up patch fixes.
Allow automatic patch updates from the noble stack.Apply:
"dependencies": { "@onekeyfe/hd-core": "1.1.10", "@onekeyfe/hd-shared": "1.1.10", "@onekeyfe/hd-transport": "1.1.10", - "@stoprocent/noble": "2.3.4", + "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" },
28-33
: Optional: switch internal deps to workspace protocol.
Reduces release friction in the monorepo.Apply:
"dependencies": { - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10", + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*", "@stoprocent/noble": "^2.3.4", "p-retry": "^4.6.2" },packages/core/package.json (1)
3-5
: Add sideEffects and files.
Smaller, tree‑shakable publishes.Apply:
"version": "1.1.10", "description": "Core processes and APIs for communicating with OneKey hardware devices.", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/connect-examples/electron-example/package.json (3)
5-8
: Fix misleading package descriptionThis is a hardware example, not “End-to-end encrypted workspaces”.
"productName": "HardwareExample", "executableName": "onekey-hardware-example", "version": "1.1.10", "author": "OneKey", - "description": "End-to-end encrypted workspaces for teams", + "description": "OneKey hardware Electron example app",
25-27
: Ensure HCI socket is installed for LinuxNoble needs the HCI native binding. Add it explicitly to avoid runtime “module not found”.
"dependencies": { "@onekeyfe/hd-transport-electron": "1.1.10", "@stoprocent/noble": "2.3.4", + "@stoprocent/bluetooth-hci-socket": "^2.2.3",
18-20
: Add CI-safe build scripts (don’t publish from CI by default)Provide :ci variants to prevent accidental publishes in pipelines.
"scripts": { "rebuild:deps": "electron-builder install-app-deps", - "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", - "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish always", + "make:win": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish always", + "make:mac:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --mac --config electron-builder.config.js --publish never", + "make:win:ci": "yarn rebuild:deps && yarn clean:build && yarn build-electron-web && electron-builder build --win --config electron-builder.config.js --publish never",packages/hd-transport-electron/src/noble-ble-handler.ts (15)
713-769
: Targeted scan: don’t remove a listener you haven’t added; normalize scan errorsRemove the redundant removeListener and handle error as unknown.
@@ - // Remove any existing discover listeners to prevent memory leaks - if (noble) { - noble.removeListener('discover', onDiscover); - noble.on('discover', onDiscover); - } + if (noble) { + noble.on('discover', onDiscover); + } @@ - // Start scanning + // Start scanning if (noble) { - noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: Error) => { + noble.startScanning(ONEKEY_SERVICE_UUIDS, false, (error?: unknown) => { if (error) { clearTimeout(timeout); if (noble) { noble.removeListener('discover', onDiscover); } - logger?.error('[NobleBLE] Failed to start targeted scan:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, error.message)); + logger?.error('[NobleBLE] Failed to start targeted scan:', error); + reject(ERRORS.TypedError(HardwareErrorCode.BleScanError, (error as any)?.message ?? String(error))); return; }
21-21
: Type imports from @stoprocent/noble may be missingImport types from 'noble' or add a d.ts shim re-exporting types to avoid build breaks.
-import type { Peripheral, Service, Characteristic } from '@stoprocent/noble'; +import type { Peripheral, Service, Characteristic } from 'noble';If you prefer sticking to @stoprocent, declare a local module and re-export types.
83-83
: Make connection timeout configurable (default 3s)Let ops tune without rebuilds; 3s can be tight vs pairing probes.
-const CONNECTION_TIMEOUT = 3000; // 3 seconds for device connection +const CONNECTION_TIMEOUT = Number(process.env.ONEKEY_BLE_CONNECT_TIMEOUT_MS ?? 3000);
106-111
: Reduce notification log noiseLog notifications at debug to cut log volume and overhead.
- logger?.info('[NobleBLE] Notification', { + logger?.debug('[NobleBLE] Notification', {
863-877
: Simplify listener teardown—clear all if supportedUse removeAllListeners() if available; keep try/catch.
- try { - (noble as any).removeAllListeners('discover'); - (noble as any).removeAllListeners('stateChange'); + try { + const ee = noble as unknown as { removeAllListeners?: (event?: string) => void }; + ee.removeAllListeners?.(); logger?.info('[NobleBLE] All Noble listeners cleaned up');
1024-1031
: Force reconnect: avoid.message
on possibly non-ErrorNormalize the error string.
- reject(new Error(`Force reconnect failed: ${error.message}`)); + reject(new Error(`Force reconnect failed: ${(error as any)?.message ?? String(error)}`));
1410-1414
: Drop hard-coded subscribe context fieldsRemove always-false fields to avoid confusion.
logger?.info('[NobleBLE] Subscribe context', { deviceId, opStateBefore: subscriptionOperations.get(deviceId) || 'idle', - paired: false, - hasController: false, });
1471-1483
: Subscribe/unsubscribe callbacks: accept unknown and normalizePrevent crashes on string errors.
- await new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe(() => { - resolve(); - }); - }); - await new Promise<void>((resolve, reject) => { - notifyCharacteristic.subscribe((error?: Error) => { - if (error) { - reject(error); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.unsubscribe((error?: unknown) => { + if (error) return reject(new Error((error as any)?.message ?? String(error))); + resolve(); + }); + }); + await new Promise<void>((resolve, reject) => { + notifyCharacteristic.subscribe((error?: unknown) => { + if (error) { + reject(new Error((error as any)?.message ?? String(error))); return; } resolve(); }); });
1514-1533
: Remove stray console logs or gate behind DEBUGUse logger or env-gated console to keep production logs clean.
- console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] Attempting to set up Noble BLE handlers.'); @@ - console.log('[NobleBLE] NOBLE_VERSION_771'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] NOBLE bootstrap'); @@ - console.log('[NobleBLE] electron-log loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron-log loaded successfully.'); @@ - console.log('[NobleBLE] electron.ipcMain loaded successfully.'); + if (process.env.ONEKEY_BLE_DEBUG) console.log('[NobleBLE] electron.ipcMain loaded successfully.'); @@ - console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`); + if (process.env.ONEKEY_BLE_DEBUG) console.log(`[NobleBLE] Registering handler for: ${EOneKeyBleMessageKeys.NOBLE_BLE_ENUMERATE}`);
1581-1583
: Log byte length, not hex charshexData.length is nibbles. Report bytes.
- logger?.info('[NobleBLE] IPC WRITE', { deviceId, len: hexData.length }); + logger?.info('[NobleBLE] IPC WRITE', { deviceId, bytes: Math.floor(hexData.length / 2) });
27-27
: Typo in comment“bundlinpissues” → “bundling issues”.
-// Noble will be dynamically imported to avoid bundlinpissues +// Noble will be dynamically imported to avoid bundling issues
1354-1389
: Unsubscribe: error is unknown; always reset state (you already do—good)Widen type and keep the cleanup you have.
- return new Promise<void>(resolve => { - notifyCharacteristic.unsubscribe((error: Error | undefined) => { + return new Promise<void>(resolve => { + notifyCharacteristic.unsubscribe((error: unknown) => { if (error) { logger?.error('[NobleBLE] Notification unsubscription failed:', error);
477-492
: Write callback: treat error as unknown and normalizeAvoid assuming Error and keep logs consistent.
- return new Promise((resolve, reject) => { - writeCharacteristic.write(buffer, true, (error?: Error) => { + return new Promise((resolve, reject) => { + writeCharacteristic.write(buffer, true, (error?: unknown) => { if (error) { - logger?.error('[NobleBLE] Write failed', { deviceId, error: String(error) }); - reject(error); + logger?.error('[NobleBLE] Write failed', { deviceId, error }); + reject(new Error((error as any)?.message ?? String(error))); return; } resolve(); }); });
916-990
: Service/characteristic discovery: error should be unknown; normalize messagesPrevent crashes on non-Error values.
- (error: Error | undefined, services: Service[]) => { + (error: unknown, services: Service[]) => { if (error) { logger?.error('[NobleBLE] Service discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleServiceNotFound, (error as any)?.message ?? String(error))); return; } @@ - (error: Error | undefined, characteristics: Characteristic[]) => { + (error: unknown, characteristics: Characteristic[]) => { if (error) { logger?.error('[NobleBLE] Characteristic discovery failed:', error); - reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, error.message)); + reject(ERRORS.TypedError(HardwareErrorCode.BleCharacteristicNotFound, (error as any)?.message ?? String(error))); return; }
374-440
: cancelOperations option is a no-op—perform best-effort unsubscribe before wiping stateCancel OS notifications/listeners before deleting maps to avoid leaks and stale OS state.
function cleanupDevice( @@ - // 1. 清理设备状态 + // 1. 最佳努力取消进行中的通知/操作 + if (cancelOperations) { + try { + const ch = deviceCharacteristics.get(deviceId); + ch?.notify.removeAllListeners('data'); + ch?.notify.unsubscribe?.(() => {}); + } catch (e) { + safeLog(logger, 'warn', '[NobleBLE] Best-effort unsubscribe failed during cleanup', e); + } + } + + // 2. 清理设备状态 @@ - // 2. 发送断开事件(如果需要) + // 3. 发送断开事件(如果需要)packages/connect-examples/expo-playground/package.json (1)
20-23
: Consider workspace protocol for local devKeeps the playground in sync with the monorepo during development.
- "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-web-sdk": "1.1.10", + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-web-sdk": "workspace:^",packages/hd-web-sdk/package.json (2)
3-3
: Slim the publish and improve tree-shakingAdd files list and sideEffects flag.
"version": "1.1.10", + "sideEffects": false, + "files": [ + "build/**/*", + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],
24-28
: Align internal deps via workspace protocol (or ensure all 1.1.10s are live)This reduces churn and registry coupling.
"dependencies": { "@onekeyfe/cross-inpage-provider-core": "^0.0.17", - "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-http": "1.1.10", - "@onekeyfe/hd-transport-web-device": "1.1.10" + "@onekeyfe/hd-core": "workspace:^", + "@onekeyfe/hd-shared": "workspace:^", + "@onekeyfe/hd-transport-http": "workspace:^", + "@onekeyfe/hd-transport-web-device": "workspace:^" },packages/hd-transport-emulator/package.json (1)
29-29
: Security: upgrade axios to a patched 1.x0.27.x carries known CSRF/SSRF issues. Move to ≥1.8.2 (prefer latest 1.x). Run tests after bump.
- "axios": "^0.27.2", + "axios": "^1.11.0",#!/bin/bash # Verify no 0.x axios remains and audit rg -nP '"axios":\s*"(\^|~)?0\.' packages || echo "✓ No axios 0.x pins" cd packages/hd-transport-emulator npm install --package-lock-only --legacy-peer-deps npm audit --omit=dev || truepackages/hd-transport-http/package.json (1)
29-29
: Security: upgrade axios to a patched 1.xPlease bump axios; 0.27.x is vulnerable. Prefer latest 1.x and re-run builds.
- "axios": "^0.27.2", + "axios": "^1.11.0",#!/bin/bash cd packages/hd-transport-http npm install --package-lock-only --legacy-peer-deps npm ls axios --depth=0 || true npm audit --omit=dev | grep -i -C3 axios || echo "✓ No axios advisories found"packages/hd-transport-react-native/package.json (1)
22-23
: Optional: use workspace protocol for internal depsThis eases future coordinated bumps across the monorepo.
- "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport": "1.1.10", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport": "workspace:*",packages/hd-ble-sdk/package.json (2)
23-25
: Optional: switch internal deps to workspace protocolReduces churn during releases.
- "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-react-native": "1.1.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-react-native": "workspace:*"
3-3
: Publish hygiene: add files and sideEffectsSlimmer tarballs and better tree‑shaking.
"version": "1.1.10", + "sideEffects": false, + "files": [ + "dist/**/*", + "README.md", + "LICENSE", + "package.json" + ],packages/hd-common-connect-sdk/package.json (2)
3-3
: Release hygiene: ensure CHANGELOG entry and Git tag for 1.1.10Add notes and push the tag so downstreams can track changes.
#!/bin/bash fd -HI 'CHANGELOG.md' -t f | xargs -I{} sh -c 'echo "-- {} --"; rg -n "^##\\s*\\[?1\\.1\\.10" "{}" || true' git tag -l | rg '^v?1\.1\.10$' || echo "Missing git tag 1.1.10"
23-28
: Optional: adopt workspace protocol for internal depsMakes synchronized releases easier.
- "@onekeyfe/hd-core": "1.1.10", - "@onekeyfe/hd-shared": "1.1.10", - "@onekeyfe/hd-transport-emulator": "1.1.10", - "@onekeyfe/hd-transport-http": "1.1.10", - "@onekeyfe/hd-transport-lowlevel": "1.1.10", - "@onekeyfe/hd-transport-web-device": "1.1.10" + "@onekeyfe/hd-core": "workspace:*", + "@onekeyfe/hd-shared": "workspace:*", + "@onekeyfe/hd-transport-emulator": "workspace:*", + "@onekeyfe/hd-transport-http": "workspace:*", + "@onekeyfe/hd-transport-lowlevel": "workspace:*", + "@onekeyfe/hd-transport-web-device": "workspace:*"packages/connect-examples/expo-example/package.json (2)
22-25
: Verify pinned @onekeyfe packages are published at 1.1.10.Pins are good if they exist on npm. Please confirm to avoid broken installs outside the monorepo.
#!/bin/bash set -euo pipefail pkgs=( "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" "@onekeyfe/[email protected]" ) for p in "${pkgs[@]}"; do printf "%-45s" "$p" if npm view "$p" version >/dev/null 2>&1; then echo "published: $(npm view "$p" version)" else echo "NOT PUBLISHED" fi done echo -e "\nDist-tags:" for n in "@onekeyfe/hd-ble-sdk" "@onekeyfe/hd-common-connect-sdk" "@onekeyfe/hd-core" "@onekeyfe/hd-web-sdk"; do printf "%-35s" "$n"; npm view "$n" dist-tags --json 2>/dev/null || true done
10-10
: Use a single cross-env.One cross-env is enough; drop the duplicate for clarity.
- "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 cross-env NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js", + "build:electron-web": "cross-env NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production EXPO_ELECTRON_MODE=true yarn expo export:web && node scripts/moveWebBuild.js",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (16)
packages/connect-examples/electron-example/package.json
(2 hunks)packages/connect-examples/expo-example/package.json
(2 hunks)packages/connect-examples/expo-playground/package.json
(2 hunks)packages/core/package.json
(2 hunks)packages/hd-ble-sdk/package.json
(2 hunks)packages/hd-common-connect-sdk/package.json
(2 hunks)packages/hd-transport-electron/package.json
(2 hunks)packages/hd-transport-electron/src/noble-ble-handler.ts
(29 hunks)packages/hd-transport-emulator/package.json
(2 hunks)packages/hd-transport-http/package.json
(2 hunks)packages/hd-transport-lowlevel/package.json
(2 hunks)packages/hd-transport-react-native/package.json
(2 hunks)packages/hd-transport-web-device/package.json
(2 hunks)packages/hd-transport/package.json
(1 hunks)packages/hd-web-sdk/package.json
(2 hunks)packages/shared/package.json
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: build (22)
- GitHub Check: lint (22)
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (14)
packages/shared/package.json (1)
3-3
: Version sync looks good.
No functional concerns with the bump to 1.1.10.packages/hd-transport/package.json (1)
3-4
: Nice cleanup: version and description.
Good step toward a stable release artifact.packages/hd-transport-lowlevel/package.json (1)
3-3
: Version bump OK.
Matches the repo-wide 1.1.10 sync.packages/hd-transport-electron/package.json (1)
3-3
: Release version OK.packages/core/package.json (1)
3-5
: Good: stable version and real description.packages/hd-transport-electron/src/noble-ble-handler.ts (1)
223-268
: Robust poweredOff handling—nice workThorough state reset on Bluetooth off. This should kill many “ghost” states. LGTM.
packages/hd-transport-emulator/package.json (2)
3-3
: Version bump to 1.1.10 looks goodAligned with the rest of the stack.
27-28
: Internal deps aligned to 1.1.10Consistent pins. Good.
packages/hd-transport-http/package.json (2)
3-3
: Version bump to 1.1.10 looks goodMatches monorepo alignment.
27-28
: Internal deps aligned to 1.1.10Solid.
packages/hd-transport-react-native/package.json (1)
3-3
: Version bump to 1.1.10 looks goodRelease alignment is consistent.
packages/hd-ble-sdk/package.json (1)
3-3
: Version bump to 1.1.10 looks goodNo issues spotted.
packages/hd-common-connect-sdk/package.json (1)
3-3
: Version bump to 1.1.10 looks goodAligned with dependent packages.
packages/connect-examples/expo-example/package.json (1)
3-3
: Version bump looks fine (example is private).No action needed.
8e3b513
to
8d0b628
Compare
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Chores