Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
All notable changes to `@railgun-community/ledger-client` are documented here. This
project is pre-1.0 and experimental; expect breaking changes on minor versions.

## 0.4.1 — 2026-08-21

### Fixed

- **`NodeHIDTransport` is reachable from consumers.** The package `exports` map previously
declared only `"."`, so the deep path every Node consumer was directed to
(`dist/core/transport/nodehid-transport.js`) was blocked by Node and TypeScript
(`ERR_MODULE_NOT_FOUND` / `TS2307`), and the class was not re-exported from the root either.
Both routes the transport factory's own error message suggests — direct import and
`transportFactory` injection — were therefore impossible. Added a `./node` subpath export
and a root re-export. Use `@railgun-community/ledger-client/node` in bare Node ESM; the root
barrel reaches `@ledgerhq/errors@6.32.0`, whose `lib-es` build Node's ESM resolver rejects.
- **`@ledgerhq/hw-transport-node-hid` declared as an optional peer dependency**, matching the
existing treatment of the WebHID and Web BLE transports. It was a devDependency only, so
consumers were never told to install it.

## 0.4.0 — 2026-07-24

Grows the CLEAR_SIGN transact surface from the 0.3.0 builders into full, engine-ready
Expand Down
34 changes: 29 additions & 5 deletions docs/api/transport.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,36 @@ loaded lazily on `connect()`). `'nodehid'` throws a directional `HWError` — No
- a **user gesture** to trigger the device-selection prompt (call `connect()` from a click)
- not embedded in an iframe that blocks the `hid` Permissions-Policy

## Node — scripts & tests
## Node — NodeHIDTransport

The bundled scripts (`install-app.ts`, `test-*-live.ts`) use a Node HID transport internally
over `@ledgerhq/hw-transport-node-hid`. For unit tests, inject a **mock** transport through
the controller's `transportFactory` (see [controller.md](./controller.md#controller-options))
rather than touching real hardware.
`NodeHIDTransport` is constructed directly — the browser `createTransport` factory does not
build it. Import it from the **`/node` subpath**:

```ts
import { NodeHIDTransport } from '@railgun-community/ledger-client/node';

const transport = new NodeHIDTransport();
await transport.connect();
```

It is also re-exported from the package root, which is the convenient form under a bundler.
**In bare Node ESM, use the subpath.** The root barrel statically pulls in
`@ledgerhq/hw-transport-webhid` and `@ledgerhq/hw-transport`, which reach
`@ledgerhq/errors@6.32.0` — that package's `lib-es` build uses an extensionless relative import
that Node's ESM resolver rejects (`ERR_MODULE_NOT_FOUND` on `lib-es/helpers`). Bundlers resolve
it; bare `node` does not. The `/node` subpath sidesteps it entirely — `nodehid-transport` has no
static `@ledgerhq` imports at all.

It requires the optional peer dep `@ledgerhq/hw-transport-node-hid` (native `node-hid`
bindings), loaded lazily on `connect()` — so importing it in a browser bundle pulls in nothing
Node-specific, and consumers that never use it don't need the dep installed.

To use it through the controller, inject it:
`createLedgerController({ transportFactory: () => new NodeHIDTransport() })`.

The bundled scripts (`install-app.ts`, `test-*-live.ts`) use this transport internally. For unit
tests, inject a **mock** transport through the controller's `transportFactory` (see
[controller.md](./controller.md#controller-options)) rather than touching real hardware.

## Choosing / injecting a transport

Expand Down
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"./node": {
"types": "./dist/core/transport/nodehid-transport.d.ts",
"import": "./dist/core/transport/nodehid-transport.js"
},
"./package.json": "./package.json"
},
"sideEffects": false,
"files": [
Expand Down Expand Up @@ -61,13 +66,17 @@
"peerDependencies": {
"@ledgerhq/hw-app-eth": "^6.41.0",
"@ledgerhq/hw-transport": "^6.31.0",
"@ledgerhq/hw-transport-node-hid": "^6.32.1",
"@ledgerhq/hw-transport-web-ble": "^6.30.0",
"@ledgerhq/hw-transport-webhid": "^6.30.0"
},
"peerDependenciesMeta": {
"@ledgerhq/hw-app-eth": {
"optional": true
},
"@ledgerhq/hw-transport-node-hid": {
"optional": true
},
"@ledgerhq/hw-transport-web-ble": {
"optional": true
},
Expand Down
2 changes: 1 addition & 1 deletion src/core/transport/transport-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export function createTransport(config?: TransportConfig): HWTransport {
case 'nodehid':
throw new HWError(
HWErrorCode.TRANSPORT_NOT_AVAILABLE,
'Node HID transport is Node-only and not constructed by the browser factory. Import NodeHIDTransport directly in a Node context, or inject it via the controller transportFactory option.',
'Node HID transport is Node-only and not constructed by the browser factory. Import it in a Node context (`import { NodeHIDTransport } from "@railgun-community/ledger-client/node"`), or inject it via the controller transportFactory option.',
);

case 'ble':
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ export {
export { createTransport } from './core/transport/transport-factory.js';
export { WebHIDTransport } from './core/transport/webhid-transport.js';
export { WebBLETransport } from './core/transport/web-ble-transport.js';
export { NodeHIDTransport } from './core/transport/nodehid-transport.js';

// ─── Device-state recovery ───────────────────────────────────────────────────
export { clearDeviceState } from './core/transport/clear-state.js';
Expand Down
Loading