@ledgerhq/hw-transport-webusb
Advanced tools
Comparing version 4.74.3-alpha.6 to 4.77.0
@@ -6,8 +6,13 @@ "use strict"; | ||
}); | ||
exports.default = void 0; | ||
var _hwTransport = _interopRequireDefault(require("@ledgerhq/hw-transport")); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var _hidFraming = _interopRequireDefault(require("@ledgerhq/devices/lib/hid-framing")); | ||
var _hwTransport = require("@ledgerhq/hw-transport"); | ||
var _hwTransport2 = _interopRequireDefault(_hwTransport); | ||
var _hidFraming = require("@ledgerhq/devices/lib/hid-framing"); | ||
var _hidFraming2 = _interopRequireDefault(_hidFraming); | ||
var _devices = require("@ledgerhq/devices"); | ||
@@ -23,4 +28,13 @@ | ||
const configurationValue = 1; | ||
const endpointNumber = 3; | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
var configurationValue = 1; | ||
var endpointNumber = 3; | ||
/** | ||
@@ -34,60 +48,18 @@ * WebUSB Transport implementation | ||
class TransportWebUSB extends _hwTransport.default { | ||
constructor(device, interfaceNumber) { | ||
super(); | ||
this.device = void 0; | ||
this.deviceModel = void 0; | ||
this.channel = Math.floor(Math.random() * 0xffff); | ||
this.packetSize = 64; | ||
this.interfaceNumber = void 0; | ||
this._disconnectEmitted = false; | ||
var TransportWebUSB = function (_Transport) { | ||
_inherits(TransportWebUSB, _Transport); | ||
this._emitDisconnect = e => { | ||
if (this._disconnectEmitted) return; | ||
this._disconnectEmitted = true; | ||
this.emit("disconnect", e); | ||
}; | ||
function TransportWebUSB(device, interfaceNumber) { | ||
_classCallCheck(this, TransportWebUSB); | ||
this.exchange = apdu => this.exchangeAtomicImpl(async () => { | ||
const { | ||
channel, | ||
packetSize | ||
} = this; | ||
(0, _logs.log)("apdu", "=> " + apdu.toString("hex")); | ||
const framing = (0, _hidFraming.default)(channel, packetSize); // Write... | ||
var _this = _possibleConstructorReturn(this, (TransportWebUSB.__proto__ || Object.getPrototypeOf(TransportWebUSB)).call(this)); | ||
const blocks = framing.makeBlocks(apdu); | ||
_initialiseProps.call(_this); | ||
for (let i = 0; i < blocks.length; i++) { | ||
(0, _logs.log)("hid-frame", "=> " + blocks[i].toString("hex")); | ||
await this.device.transferOut(endpointNumber, blocks[i]); | ||
} // Read... | ||
_this.device = device; | ||
_this.interfaceNumber = interfaceNumber; | ||
_this.deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
return _this; | ||
} | ||
let result; | ||
let acc; | ||
while (!(result = framing.getReducedResult(acc))) { | ||
const r = await this.device.transferIn(endpointNumber, packetSize); | ||
const buffer = Buffer.from(r.data.buffer); | ||
(0, _logs.log)("hid-frame", "<= " + buffer.toString("hex")); | ||
acc = framing.reduceResponse(acc, buffer); | ||
} | ||
(0, _logs.log)("apdu", "<= " + result.toString("hex")); | ||
return result; | ||
}).catch(e => { | ||
if (e && e.message && e.message.includes("disconnected")) { | ||
this._emitDisconnect(e); | ||
throw new _errors.DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
}); | ||
this.device = device; | ||
this.interfaceNumber = interfaceNumber; | ||
this.deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
} | ||
/** | ||
@@ -99,101 +71,256 @@ * Check if WebUSB transport is supported. | ||
/** | ||
* Similar to create() except it will always display the device permission (even if some devices are already accepted). | ||
* List the WebUSB devices that was previously authorized by the user. | ||
*/ | ||
static async request() { | ||
const device = await (0, _webusb.requestLedgerDevice)(); | ||
return TransportWebUSB.open(device); | ||
} | ||
/** | ||
* Similar to create() except it will never display the device permission (it returns a Promise<?Transport>, null if it fails to find a device). | ||
*/ | ||
static async openConnected() { | ||
const devices = await (0, _webusb.getLedgerDevices)(); | ||
if (devices.length === 0) return null; | ||
return TransportWebUSB.open(devices[0]); | ||
} | ||
/** | ||
* Create a Ledger transport with a USBDevice | ||
* Actively listen to WebUSB devices and emit ONE device | ||
* that was either accepted before, if not it will trigger the native permission UI. | ||
* | ||
* Important: it must be called in the context of a UI click! | ||
*/ | ||
static async open(device) { | ||
await device.open(); | ||
_createClass(TransportWebUSB, [{ | ||
key: "close", | ||
if (device.configuration === null) { | ||
await device.selectConfiguration(configurationValue); | ||
} | ||
await device.reset(); | ||
const iface = device.configurations[0].interfaces.find(({ | ||
alternates | ||
}) => alternates.some(a => a.interfaceClass === 255)); | ||
/** | ||
* Release the transport device | ||
*/ | ||
value: function () { | ||
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | ||
return regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
_context.next = 2; | ||
return this.exchangeBusyPromise; | ||
if (!iface) { | ||
throw new _errors.TransportInterfaceNotAvailable("No WebUSB interface found for your Ledger device. Please upgrade firmware or contact techsupport."); | ||
} | ||
case 2: | ||
_context.next = 4; | ||
return this.device.releaseInterface(this.interfaceNumber); | ||
const interfaceNumber = iface.interfaceNumber; | ||
case 4: | ||
_context.next = 6; | ||
return this.device.reset(); | ||
try { | ||
await device.claimInterface(interfaceNumber); | ||
} catch (e) { | ||
await device.close(); | ||
throw new _errors.TransportInterfaceNotAvailable(e.message); | ||
} | ||
case 6: | ||
_context.next = 8; | ||
return this.device.close(); | ||
const transport = new TransportWebUSB(device, interfaceNumber); | ||
case 8: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, this); | ||
})); | ||
const onDisconnect = e => { | ||
if (device === e.device) { | ||
// $FlowFixMe | ||
navigator.usb.removeEventListener("disconnect", onDisconnect); | ||
function close() { | ||
return _ref.apply(this, arguments); | ||
} | ||
transport._emitDisconnect(new _errors.DisconnectedDevice()); | ||
return close; | ||
}() | ||
/** | ||
* Exchange with the device using APDU protocol. | ||
* @param apdu | ||
* @returns a promise of apdu response | ||
*/ | ||
}, { | ||
key: "setScrambleKey", | ||
value: function setScrambleKey() {} | ||
}], [{ | ||
key: "request", | ||
/** | ||
* Similar to create() except it will always display the device permission (even if some devices are already accepted). | ||
*/ | ||
value: function () { | ||
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { | ||
var device; | ||
return regeneratorRuntime.wrap(function _callee2$(_context2) { | ||
while (1) { | ||
switch (_context2.prev = _context2.next) { | ||
case 0: | ||
_context2.next = 2; | ||
return (0, _webusb.requestLedgerDevice)(); | ||
case 2: | ||
device = _context2.sent; | ||
return _context2.abrupt("return", TransportWebUSB.open(device)); | ||
case 4: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
} | ||
}, _callee2, this); | ||
})); | ||
function request() { | ||
return _ref2.apply(this, arguments); | ||
} | ||
}; // $FlowFixMe | ||
return request; | ||
}() | ||
navigator.usb.addEventListener("disconnect", onDisconnect); | ||
return transport; | ||
} | ||
/** | ||
* Similar to create() except it will never display the device permission (it returns a Promise<?Transport>, null if it fails to find a device). | ||
*/ | ||
/** | ||
* Release the transport device | ||
*/ | ||
async close() { | ||
await this.exchangeBusyPromise; | ||
await this.device.releaseInterface(this.interfaceNumber); | ||
await this.device.reset(); | ||
await this.device.close(); | ||
} | ||
/** | ||
* Exchange with the device using APDU protocol. | ||
* @param apdu | ||
* @returns a promise of apdu response | ||
*/ | ||
}, { | ||
key: "openConnected", | ||
value: function () { | ||
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { | ||
var devices; | ||
return regeneratorRuntime.wrap(function _callee3$(_context3) { | ||
while (1) { | ||
switch (_context3.prev = _context3.next) { | ||
case 0: | ||
_context3.next = 2; | ||
return (0, _webusb.getLedgerDevices)(); | ||
case 2: | ||
devices = _context3.sent; | ||
setScrambleKey() {} | ||
if (!(devices.length === 0)) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
} | ||
return _context3.abrupt("return", null); | ||
exports.default = TransportWebUSB; | ||
case 5: | ||
return _context3.abrupt("return", TransportWebUSB.open(devices[0])); | ||
case 6: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
} | ||
}, _callee3, this); | ||
})); | ||
function openConnected() { | ||
return _ref3.apply(this, arguments); | ||
} | ||
return openConnected; | ||
}() | ||
/** | ||
* Create a Ledger transport with a USBDevice | ||
*/ | ||
}, { | ||
key: "open", | ||
value: function () { | ||
var _ref4 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4(device) { | ||
var iface, interfaceNumber, transport, onDisconnect; | ||
return regeneratorRuntime.wrap(function _callee4$(_context4) { | ||
while (1) { | ||
switch (_context4.prev = _context4.next) { | ||
case 0: | ||
_context4.next = 2; | ||
return device.open(); | ||
case 2: | ||
if (!(device.configuration === null)) { | ||
_context4.next = 5; | ||
break; | ||
} | ||
_context4.next = 5; | ||
return device.selectConfiguration(configurationValue); | ||
case 5: | ||
_context4.next = 7; | ||
return device.reset(); | ||
case 7: | ||
iface = device.configurations[0].interfaces.find(function (_ref5) { | ||
var alternates = _ref5.alternates; | ||
return alternates.some(function (a) { | ||
return a.interfaceClass === 255; | ||
}); | ||
}); | ||
if (iface) { | ||
_context4.next = 10; | ||
break; | ||
} | ||
throw new _errors.TransportInterfaceNotAvailable("No WebUSB interface found for your Ledger device. Please upgrade firmware or contact techsupport."); | ||
case 10: | ||
interfaceNumber = iface.interfaceNumber; | ||
_context4.prev = 11; | ||
_context4.next = 14; | ||
return device.claimInterface(interfaceNumber); | ||
case 14: | ||
_context4.next = 21; | ||
break; | ||
case 16: | ||
_context4.prev = 16; | ||
_context4.t0 = _context4["catch"](11); | ||
_context4.next = 20; | ||
return device.close(); | ||
case 20: | ||
throw new _errors.TransportInterfaceNotAvailable(_context4.t0.message); | ||
case 21: | ||
transport = new TransportWebUSB(device, interfaceNumber); | ||
onDisconnect = function onDisconnect(e) { | ||
if (device === e.device) { | ||
// $FlowFixMe | ||
navigator.usb.removeEventListener("disconnect", onDisconnect); | ||
transport._emitDisconnect(new _errors.DisconnectedDevice()); | ||
} | ||
}; | ||
// $FlowFixMe | ||
navigator.usb.addEventListener("disconnect", onDisconnect); | ||
return _context4.abrupt("return", transport); | ||
case 25: | ||
case "end": | ||
return _context4.stop(); | ||
} | ||
} | ||
}, _callee4, this, [[11, 16]]); | ||
})); | ||
function open(_x) { | ||
return _ref4.apply(this, arguments); | ||
} | ||
return open; | ||
}() | ||
}]); | ||
return TransportWebUSB; | ||
}(_hwTransport2.default); | ||
TransportWebUSB.isSupported = _webusb.isSupported; | ||
TransportWebUSB.list = _webusb.getLedgerDevices; | ||
TransportWebUSB.listen = observer => { | ||
let unsubscribed = false; | ||
(0, _webusb.getFirstLedgerDevice)().then(device => { | ||
TransportWebUSB.listen = function (observer) { | ||
var unsubscribed = false; | ||
(0, _webusb.getFirstLedgerDevice)().then(function (device) { | ||
if (!unsubscribed) { | ||
const deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
observer.next({ | ||
type: "add", | ||
descriptor: device, | ||
deviceModel | ||
}); | ||
var deviceModel = (0, _devices.identifyUSBProductId)(device.productId); | ||
observer.next({ type: "add", descriptor: device, deviceModel: deviceModel }); | ||
observer.complete(); | ||
} | ||
}, error => { | ||
}, function (error) { | ||
if (window.DOMException && error instanceof window.DOMException && error.code === 18) { | ||
@@ -205,11 +332,100 @@ observer.error(new _errors.TransportWebUSBGestureRequired(error.message)); | ||
}); | ||
function unsubscribe() { | ||
unsubscribed = true; | ||
} | ||
return { unsubscribe: unsubscribe }; | ||
}; | ||
return { | ||
unsubscribe | ||
var _initialiseProps = function _initialiseProps() { | ||
var _this2 = this; | ||
this.channel = Math.floor(Math.random() * 0xffff); | ||
this.packetSize = 64; | ||
this._disconnectEmitted = false; | ||
this._emitDisconnect = function (e) { | ||
if (_this2._disconnectEmitted) return; | ||
_this2._disconnectEmitted = true; | ||
_this2.emit("disconnect", e); | ||
}; | ||
this.exchange = function (apdu) { | ||
return _this2.exchangeAtomicImpl(_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee5() { | ||
var channel, packetSize, framing, blocks, i, result, acc, r, buffer; | ||
return regeneratorRuntime.wrap(function _callee5$(_context5) { | ||
while (1) { | ||
switch (_context5.prev = _context5.next) { | ||
case 0: | ||
channel = _this2.channel, packetSize = _this2.packetSize; | ||
(0, _logs.log)("apdu", "=> " + apdu.toString("hex")); | ||
framing = (0, _hidFraming2.default)(channel, packetSize); | ||
// Write... | ||
blocks = framing.makeBlocks(apdu); | ||
i = 0; | ||
case 5: | ||
if (!(i < blocks.length)) { | ||
_context5.next = 12; | ||
break; | ||
} | ||
(0, _logs.log)("hid-frame", "=> " + blocks[i].toString("hex")); | ||
_context5.next = 9; | ||
return _this2.device.transferOut(endpointNumber, blocks[i]); | ||
case 9: | ||
i++; | ||
_context5.next = 5; | ||
break; | ||
case 12: | ||
// Read... | ||
result = void 0; | ||
acc = void 0; | ||
case 14: | ||
if (result = framing.getReducedResult(acc)) { | ||
_context5.next = 23; | ||
break; | ||
} | ||
_context5.next = 17; | ||
return _this2.device.transferIn(endpointNumber, packetSize); | ||
case 17: | ||
r = _context5.sent; | ||
buffer = Buffer.from(r.data.buffer); | ||
(0, _logs.log)("hid-frame", "<= " + buffer.toString("hex")); | ||
acc = framing.reduceResponse(acc, buffer); | ||
_context5.next = 14; | ||
break; | ||
case 23: | ||
(0, _logs.log)("apdu", "<= " + result.toString("hex")); | ||
return _context5.abrupt("return", result); | ||
case 25: | ||
case "end": | ||
return _context5.stop(); | ||
} | ||
} | ||
}, _callee5, _this2); | ||
}))).catch(function (e) { | ||
if (e && e.message && e.message.includes("disconnected")) { | ||
_this2._emitDisconnect(e); | ||
throw new _errors.DisconnectedDeviceDuringOperation(e.message); | ||
} | ||
throw e; | ||
}); | ||
}; | ||
}; | ||
exports.default = TransportWebUSB; | ||
//# sourceMappingURL=TransportWebUSB.js.map |
@@ -6,37 +6,107 @@ "use strict"; | ||
}); | ||
exports.requestLedgerDevice = requestLedgerDevice; | ||
exports.getLedgerDevices = getLedgerDevices; | ||
exports.getFirstLedgerDevice = getFirstLedgerDevice; | ||
exports.isSupported = void 0; | ||
exports.isSupported = exports.getFirstLedgerDevice = exports.getLedgerDevices = exports.requestLedgerDevice = undefined; | ||
var _devices = require("@ledgerhq/devices"); | ||
var requestLedgerDevice = exports.requestLedgerDevice = function () { | ||
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { | ||
var device; | ||
return regeneratorRuntime.wrap(function _callee$(_context) { | ||
while (1) { | ||
switch (_context.prev = _context.next) { | ||
case 0: | ||
_context.next = 2; | ||
return navigator.usb.requestDevice({ filters: ledgerDevices }); | ||
const ledgerDevices = [{ | ||
vendorId: _devices.ledgerUSBVendorId | ||
}]; | ||
case 2: | ||
device = _context.sent; | ||
return _context.abrupt("return", device); | ||
async function requestLedgerDevice() { | ||
// $FlowFixMe | ||
const device = await navigator.usb.requestDevice({ | ||
filters: ledgerDevices | ||
}); | ||
return device; | ||
} | ||
case 4: | ||
case "end": | ||
return _context.stop(); | ||
} | ||
} | ||
}, _callee, this); | ||
})); | ||
async function getLedgerDevices() { | ||
// $FlowFixMe | ||
const devices = await navigator.usb.getDevices(); | ||
return devices.filter(d => d.vendorId === _devices.ledgerUSBVendorId); | ||
} | ||
return function requestLedgerDevice() { | ||
return _ref.apply(this, arguments); | ||
}; | ||
}(); | ||
async function getFirstLedgerDevice() { | ||
const existingDevices = await getLedgerDevices(); | ||
if (existingDevices.length > 0) return existingDevices[0]; | ||
return requestLedgerDevice(); | ||
} | ||
var getLedgerDevices = exports.getLedgerDevices = function () { | ||
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() { | ||
var devices; | ||
return regeneratorRuntime.wrap(function _callee2$(_context2) { | ||
while (1) { | ||
switch (_context2.prev = _context2.next) { | ||
case 0: | ||
_context2.next = 2; | ||
return navigator.usb.getDevices(); | ||
const isSupported = () => Promise.resolve(!!navigator && // $FlowFixMe | ||
!!navigator.usb && typeof navigator.usb.getDevices === "function"); | ||
case 2: | ||
devices = _context2.sent; | ||
return _context2.abrupt("return", devices.filter(function (d) { | ||
return d.vendorId === _devices.ledgerUSBVendorId; | ||
})); | ||
exports.isSupported = isSupported; | ||
case 4: | ||
case "end": | ||
return _context2.stop(); | ||
} | ||
} | ||
}, _callee2, this); | ||
})); | ||
return function getLedgerDevices() { | ||
return _ref2.apply(this, arguments); | ||
}; | ||
}(); | ||
var getFirstLedgerDevice = exports.getFirstLedgerDevice = function () { | ||
var _ref3 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() { | ||
var existingDevices; | ||
return regeneratorRuntime.wrap(function _callee3$(_context3) { | ||
while (1) { | ||
switch (_context3.prev = _context3.next) { | ||
case 0: | ||
_context3.next = 2; | ||
return getLedgerDevices(); | ||
case 2: | ||
existingDevices = _context3.sent; | ||
if (!(existingDevices.length > 0)) { | ||
_context3.next = 5; | ||
break; | ||
} | ||
return _context3.abrupt("return", existingDevices[0]); | ||
case 5: | ||
return _context3.abrupt("return", requestLedgerDevice()); | ||
case 6: | ||
case "end": | ||
return _context3.stop(); | ||
} | ||
} | ||
}, _callee3, this); | ||
})); | ||
return function getFirstLedgerDevice() { | ||
return _ref3.apply(this, arguments); | ||
}; | ||
}(); | ||
var _devices = require("@ledgerhq/devices"); | ||
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } | ||
var ledgerDevices = [{ vendorId: _devices.ledgerUSBVendorId }]; | ||
var isSupported = exports.isSupported = function isSupported() { | ||
return Promise.resolve(!!navigator && | ||
// $FlowFixMe | ||
!!navigator.usb && typeof navigator.usb.getDevices === "function"); | ||
}; | ||
//# sourceMappingURL=webusb.js.map |
{ | ||
"name": "@ledgerhq/hw-transport-webusb", | ||
"version": "4.74.3-alpha.6+0750e69", | ||
"version": "4.77.0", | ||
"description": "Ledger Hardware Wallet WebUSB implementation of the communication layer", | ||
@@ -27,6 +27,6 @@ "keywords": [ | ||
"dependencies": { | ||
"@ledgerhq/devices": "^4.74.3-alpha.6+0750e69", | ||
"@ledgerhq/errors": "^4.74.3-alpha.6+0750e69", | ||
"@ledgerhq/hw-transport": "^4.74.3-alpha.6+0750e69", | ||
"@ledgerhq/logs": "^4.74.3-alpha.6+0750e69" | ||
"@ledgerhq/devices": "^4.77.0", | ||
"@ledgerhq/errors": "^4.77.0", | ||
"@ledgerhq/hw-transport": "^4.77.0", | ||
"@ledgerhq/logs": "^4.72.0" | ||
}, | ||
@@ -43,3 +43,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "0750e6985bbd4a0faec44c9014352a46b094c66e" | ||
"gitHead": "b25792920f55ec855fd74778ce37240ef7dbde01" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
66110
775
0
0
- Removed@ledgerhq/logs@4.74.3-alpha.6(transitive)
Updated@ledgerhq/devices@^4.77.0
Updated@ledgerhq/errors@^4.77.0
Updated@ledgerhq/logs@^4.72.0