{"id":13454253,"url":"https://github.com/node-usb/node-usb","last_synced_at":"2025-05-12T20:51:28.983Z","repository":{"id":3146551,"uuid":"4176043","full_name":"node-usb/node-usb","owner":"node-usb","description":"Improved USB library for Node.js","archived":false,"fork":false,"pushed_at":"2025-05-05T16:06:03.000Z","size":7050,"stargazers_count":1615,"open_issues_count":31,"forks_count":287,"subscribers_count":40,"default_branch":"main","last_synced_at":"2025-05-10T00:30:45.951Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://node-usb.github.io/node-usb/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/node-usb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2012-04-29T17:23:53.000Z","updated_at":"2025-05-07T08:48:20.000Z","dependencies_parsed_at":"2024-01-17T09:19:50.723Z","dependency_job_id":"36edcf88-bde5-4642-998e-b83be1ac9651","html_url":"https://github.com/node-usb/node-usb","commit_stats":{"total_commits":658,"total_committers":55,"mean_commits":"11.963636363636363","dds":0.7218844984802432,"last_synced_commit":"ae4baaf417bd9a4226476733e74a3a001b07f259"},"previous_names":["nonolith/node-usb","tessel/node-usb"],"tags_count":94,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-usb%2Fnode-usb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-usb%2Fnode-usb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-usb%2Fnode-usb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/node-usb%2Fnode-usb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/node-usb","download_url":"https://codeload.github.com/node-usb/node-usb/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253504557,"owners_count":21918831,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T08:00:52.259Z","updated_at":"2025-05-12T20:51:28.934Z","avatar_url":"https://github.com/node-usb.png","language":"TypeScript","readme":"# USB Library for Node.JS\n\n[![Build Status](https://github.com/node-usb/node-usb/workflows/prebuild/badge.svg)](https://github.com/node-usb/node-usb/actions)\n[![npm](https://img.shields.io/npm/dm/usb.svg)](https://www.npmjs.com/package/usb)\n[![Licence MIT](https://img.shields.io/badge/licence-MIT-blue.svg)](http://opensource.org/licenses/MIT)\n\nNode.JS library for communicating with USB devices.\n\nThis is a refactoring / rewrite of Christopher Klein's [node-usb](https://github.com/schakko/node-usb).\n\n# Prerequisites\n\n[Node.js \u003e= v10.20.0](https://nodejs.org), which includes `npm`.\n\n## Windows\n\nOn Windows, if you get `LIBUSB_ERROR_NOT_SUPPORTED` when attempting to open your device, it's possible your device doesn't have a WinUSB driver for libusb to use.\n\nYou can install one using [Zadig](http://zadig.akeo.ie/) or another approach is to use the [UsbDK Backend](https://github.com/daynix/UsbDk) of libusb by immediately calling `usb.useUsbDkBackend()`.\n\nNote that you cannot use multiple drivers on Windows as they get exclusive access to the device. So if you want to switch between drivers (e.g. using a printer with this software or the system), you will need to uninstall/install drivers as required.\n\nFor further info, check [How to use libusb on Windows](https://github.com/libusb/libusb/wiki/Windows#user-content-How_to_use_libusb_on_Windows) in the libusb's wiki.\n\n## Linux\n\nOn Linux, you'll need libudev to build libusb if a prebuild is not available. On Ubuntu/Debian:\n\n```bash\nsudo apt-get install build-essential libudev-dev\n```\n\nYou may need to modify your udev and permission rules in order to access your desired device. Along the lines of:\n\nSUBSYSTEM==\"usb\", ATTR{idVendor}==\"USB-VENDOR-ID\", ATTR{idProduct}==\"USB-PRODUCT-ID\", MODE=\"0660\", GROUP=\"GROUP-YOUR-USER-IS-IN\"\n\n# Troubleshooting\n\nFor libusb issues, please refer to the FAQ at https://github.com/libusb/libusb/wiki/FAQ\n\n# Installation\n\nNative modules are bundled using `prebuildify`, so installation should be as simple as installing the package.\n\nWith `npm`:\n\n```bash\nnpm install usb\n```\n\nWith `yarn`:\n\n```bash\nyarn add usb\n```\n\n__Note:__ the library is now written in `TypeScript`, so a separate types file is not longer required to be installed (e.g. don't install `@types/usb`).\n\n# License\n[MIT](LICENSE.md)\n\nNote that the compiled Node extension includes [libusb](https://github.com/libusb/libusb), and is thus subject to the [LGPL](https://github.com/libusb/libusb/blob/main/COPYING).\n\n# Limitations\nDoes not support:\n\n- Configurations other than the default one\n- Isochronous transfers\n\n# Getting Started\nUse the following examples to kickstart your development. Once you have a desired device, use the APIs below to interact with it.\n\n## Migrating to `v2.0.0`\nThe legacy API exists on an object called `usb` on the main import and the convenience functions exist as top level objects.\n\nTo use `v2.0.0` simply update your import statements and the function calls;\n\n```typescript\n// import * as usb from 'usb';\n// const devices: usb.Device[] = usb.getDeviceList();\n\nimport { usb, getDeviceList } from 'usb';\nconst devices: usb.Device[] = getDeviceList();\n```\n\n## List all legacy devices\n```typescript\nimport { getDeviceList } from 'usb';\n\nconst devices = getDeviceList();\n\nfor (const device of devices) {\n    console.log(device); // Legacy device\n}\n```\n\n## Find legacy device by vid/pid\n```typescript\nimport { findByIds } from 'usb';\n\nconst device = findByIds(0x59e3, 0x0a23);\n\nif (device) {\n    console.log(device); // Legacy device\n}\n```\n\n## Find legacy device by SerialNumber\n```typescript\nimport { findBySerialNumber } from 'usb';\n\n(async () =\u003e {\n    // Uses a blocking call, so is async\n    const device = await findBySerialNumber('TEST_DEVICE');\n\n    if (device) {\n        console.log(device); // Legacy device\n    }\n})();\n```\n\n## Turn legacy Device into WebUSB compatible device\n```typescript\nimport { findBySerialNumber, WebUSBDevice } from 'usb';\n\n(async () =\u003e {\n    // Uses a blocking call, so is async\n    const device = await findBySerialNumber('TEST_DEVICE');\n\n    // Uses blocking calls, so is async\n    const webDevice = await WebUSBDevice.createInstance(device);\n\n    if (webDevice) {\n        console.log(webDevice); // WebUSB device\n    }\n})();\n```\n\n## Use WebUSB approach to find a device\n```typescript\nimport { webusb } from 'usb';\n\n(async () =\u003e {\n    // Returns first matching device\n    const device = await webusb.requestDevice({\n        filters: [{}]\n    })\n\n    console.log(device); // WebUSB device\n})();\n```\n\n## Use WebUSB approach to find a device with custom selection method\n```typescript\nimport { WebUSB } from 'usb';\n\n(async () =\u003e {\n    const customWebUSB = new WebUSB({\n        // This function can return a promise which allows a UI to be displayed if required\n        devicesFound: devices =\u003e devices.find(device =\u003e device.serialNumber === 'TEST_DEVICE')\n    });\n\n    // Returns device based on injected 'devicesFound' function\n    const device = await customWebUSB.requestDevice({\n        filters: [{}]\n    })\n\n    console.log(device); // WebUSB device\n})();\n```\n\n## Use WebUSB approach to list authorised devices\n```typescript\nimport { webusb } from 'usb';\n\n(async () =\u003e {\n    // The default webusb instance follows the WebUSB spec and only returns authorised devices\n    const devices = await webusb.getDevices();\n\n    for (const device of devices) {\n        console.log(device); // WebUSB device\n    }\n})();\n```\n\n## Use WebUSB approach to list all devices\n```typescript\nimport { WebUSB } from 'usb';\n\n(async () =\u003e {\n    const customWebUSB = new WebUSB({\n        // Bypass checking for authorised devices\n        allowAllDevices: true\n    });\n\n    // Uses blocking calls, so is async\n    const devices = await customWebUSB.getDevices();\n\n    for (const device of devices) {\n        console.log(device); // WebUSB device\n    }\n})();\n```\n\n## Electron\nPlease refer to the maintained example for using `node-usb` in electron:\n\nhttps://github.com/node-usb/node-usb-example-electron\n\nIf using a packaging system for electron, ensure the `node-usb` library does not get recompiled as the correct binaries are already shipped with the package. For example, for [electron-builder](https://www.electron.build/), use these settings:\n\n- buildDependenciesFromSource: true\n- nodeGypRebuild: false\n- npmRebuild: false\n\n# APIs\nSince `v2.0.0`, the `node-usb` library supports two APIs:\n\n- `WebUSB` which follows the [WebUSB Specification](https://wicg.github.io/webusb/) (recommended)\n- `Legacy API` which retains the previous 'non-blocking' API\n\nConvenience methods also exist to easily list or find devices as well as convert between a legacy usb.Device device and WebUSB device.\n\nFull auto-generated API documentation can be seen here:\n\nhttps://node-usb.github.io/node-usb/\n\n## Convenience Functions\n\n### getDeviceList()\nReturn a list of legacy `Device` objects for the USB devices attached to the system.\n\n### findByIds(vid, pid)\nConvenience method to get the first legacy device with the specified VID and PID, or `undefined` if no such device is present.\n\n### findBySerialNumber(serialNumber)\nConvenience method to get a promise of the legacy device with the specified serial number, or `undefined` if no such device is present.\n\n### getWebUsb()\nReturn the `navigator.usb` instance if it exists, otherwise a `webusb` instance.\n\n### WebUSBDevice\nWebUSB Device class for wrapping a legacy Device into a WebUSB device\n\n#### WebUSBDevice.createInstance(device)\nConvenience method to return a promise of a WebUSB device based on a legacy device\n\n## WebUSB\n\nPlease refer to the WebUSB specification which be found here:\n\nhttps://wicg.github.io/webusb/\n\n### Implementation Status\n\n#### USB\n\n- [x] getDevices()\n- [x] requestDevice()\n\n#### USBDevice\n\n- [x] usbVersionMajor\n- [x] usbVersionMinor\n- [x] usbVersionSubminor\n- [x] deviceClass\n- [x] deviceSubclass\n- [x] deviceProtocol\n- [x] vendorId\n- [x] productId\n- [x] deviceVersionMajor\n- [x] deviceVersionMinor\n- [x] deviceVersionSubminor\n- [x] manufacturerName\n- [x] productName\n- [x] serialNumber\n- [x] configuration\n- [x] configurations\n- [x] opened\n- [x] open()\n- [x] close()\n- [x] selectConfiguration()\n- [x] claimInterface()\n- [x] releaseInterface()\n- [x] selectAlternateInterface()\n- [x] controlTransferIn()\n- [x] controlTransferOut() - `bytesWritten` always equals the initial buffer length\n- [x] transferIn()\n- [x] transferOut() - `bytesWritten` always equals the initial buffer length\n- [x] clearHalt()\n- [x] reset()\n- [ ] isochronousTransferIn()\n- [ ] isochronousTransferOut()\n- [ ] forget()\n\n#### Events\n\n- [x] connect\n- [x] disconnect\n\n## Legacy API\n\n### usb\nLegacy usb object.\n\n#### usb.LIBUSB_*\nConstant properties from libusb\n\n#### usb.getDeviceList()\nReturn a list of legacy `Device` objects for the USB devices attached to the system.\n\n#### usb.pollHotplug\nForce polling loop for hotplug events.\n\n#### usb.setDebugLevel(level : int)\nSet the libusb debug level (between 0 and 4)\n\n#### usb.useUsbDkBackend()\nOn Windows, use the USBDK backend of libusb instead of WinUSB\n\n### Device\nRepresents a USB device.\n\n#### .busNumber\nInteger USB device number\n\n#### .deviceAddress\nInteger USB device address\n\n#### .portNumbers\nArray containing the USB device port numbers, or `undefined` if not supported on this platform.\n\n#### .deviceDescriptor\nObject with properties for the fields of the device descriptor:\n\n- bLength\n- bDescriptorType\n- bcdUSB\n- bDeviceClass\n- bDeviceSubClass\n- bDeviceProtocol\n- bMaxPacketSize0\n- idVendor\n- idProduct\n- bcdDevice\n- iManufacturer\n- iProduct\n- iSerialNumber\n- bNumConfigurations\n\n#### .configDescriptor\nObject with properties for the fields of the configuration descriptor:\n\n- bLength\n- bDescriptorType\n- wTotalLength\n- bNumInterfaces\n- bConfigurationValue\n- iConfiguration\n- bmAttributes\n- bMaxPower\n- extra (Buffer containing any extra data or additional descriptors)\n\n#### .allConfigDescriptors\nContains all config descriptors of the device (same structure as .configDescriptor above)\n\n#### .parent\nContains the parent of the device, such as a hub. If there is no parent this property is set to `null`.\n\n#### .open()\nOpen the device. All methods below require the device to be open before use.\n\n#### .close()\nClose the device.\n\n#### .controlTransfer(bmRequestType, bRequest, wValue, wIndex, data_or_length, callback(error, data))\nPerform a control transfer with `libusb_control_transfer`.\n\nParameter `data_or_length` can be a integer length for an IN transfer, or a Buffer for an out transfer. The type must match the direction specified in the MSB of bmRequestType.\n\nThe `data` parameter of the callback is always undefined for OUT transfers, or will be passed a Buffer for IN transfers.\n\nA [package is available to calculate bmRequestType](https://www.npmjs.com/package/bmrequesttype) if needed.\n\n#### .setConfiguration(id, callback(error))\nSet the device configuration to something other than the default (0). To use this, first call `.open(false)` (which tells it not to auto configure), then before claiming an interface, call this method.\n\n#### .getStringDescriptor(index, callback(error, data))\nPerform a control transfer to retrieve a string descriptor\n\n#### .getBosDescriptor(callback(error, bosDescriptor))\nPerform a control transfer to retrieve an object with properties for the fields of the Binary Object Store descriptor:\n\n- bLength\n- bDescriptorType\n- wTotalLength\n- bNumDeviceCaps\n\n#### .getCapabilities(callback(error, capabilities))\nRetrieve a list of Capability objects for the Binary Object Store capabilities of the device.\n\n#### .interface(interface)\nReturn the interface with the specified interface number.\n\n#### .interfaces\nList of Interface objects for the interfaces of the default configuration of the device.\n\n#### .timeout\nTimeout in milliseconds to use for control transfers.\n\n#### .reset(callback(error))\nPerforms a reset of the device. Callback is called when complete.\n\n#### .setAutoDetachKernelDriver(enable)\nEnable/disable libusb's automatic kernel driver detachment (defaults to true in the WebUSB API)\n\n### Interface\n\n#### .endpoint(address)\nReturn the InEndpoint or OutEndpoint with the specified address.\n\n#### .endpoints\nList of endpoints on this interface: InEndpoint and OutEndpoint objects.\n\n#### .interface\nInteger interface number.\n\n#### .altSetting\nInteger alternate setting number.\n\n#### .setAltSetting(altSetting, callback(error))\nSets the alternate setting. It updates the `interface.endpoints` array to reflect the endpoints found in the alternate setting.\n\n#### .claim()\nClaims the interface. This method must be called before using any endpoints of this interface.\n\n#### .release([closeEndpoints], callback(error))\nReleases the interface and resets the alternate setting. Calls callback when complete.\n\nIt is an error to release an interface with pending transfers. If the optional closeEndpoints parameter is true, any active endpoint streams are stopped (see `Endpoint.stopStream`), and the interface is released after the stream transfers are cancelled. Transfers submitted individually with `Endpoint.transfer` are not affected by this parameter.\n\n#### .isKernelDriverActive()\nReturns `false` if a kernel driver is not active; `true` if active.\n\n#### .detachKernelDriver()\nDetaches the kernel driver from the interface.\nIf a `LIBUSB_ERROR_ACCESS` error is raised, you may need to execute this with elevated privileges.\n\n#### .attachKernelDriver()\nRe-attaches the kernel driver for the interface.\n\n#### .descriptor\nObject with fields from the interface descriptor -- see libusb documentation or USB spec.\n\n- bLength\n- bDescriptorType\n- bInterfaceNumber\n- bAlternateSetting\n- bNumEndpoints\n- bInterfaceClass\n- bInterfaceSubClass\n- bInterfaceProtocol\n- iInterface\n- extra (Buffer containing any extra data or additional descriptors)\n\n### Capability\n\n#### .type\nInteger capability type.\n\n#### .data\nBuffer capability data.\n\n#### .descriptor\nObject with fields from the capability descriptor -- see libusb documentation or USB spec.\n  \n- bLength\n- bDescriptorType\n- bDevCapabilityType\n\n### Endpoint\nCommon base for InEndpoint and OutEndpoint, see below.\n\n#### .direction\nEndpoint direction: `\"in\"` or `\"out\"`.\n\n#### .transferType\nEndpoint type: `usb.LIBUSB_TRANSFER_TYPE_BULK`, `usb.LIBUSB_TRANSFER_TYPE_INTERRUPT`, or `usb.LIBUSB_TRANSFER_TYPE_ISOCHRONOUS`.\n\n#### .descriptor\nObject with fields from the endpoint descriptor -- see libusb documentation or USB spec.\n\n- bLength\n- bDescriptorType\n- bEndpointAddress\n- bmAttributes\n- wMaxPacketSize\n- bInterval\n- bRefresh\n- bSynchAddress\n- extra (Buffer containing any extra data or additional descriptors)\n\n#### .timeout\nSets the timeout in milliseconds for transfers on this endpoint. The default, `0`, is infinite timeout.\n\n#### .clearHalt(callback(error))\nClear the halt/stall condition for this endpoint.\n\n### InEndpoint\nEndpoints in the IN direction (device-\u003ePC) have this type.\n\n#### .transfer(length, callback(error, data))\nPerform a transfer to read data from the endpoint.\n\nIf length is greater than maxPacketSize, libusb will automatically split the transfer in multiple packets, and you will receive one callback with all data once all packets are complete.\n\n`this` in the callback is the InEndpoint object.\n\n#### .startPoll(nTransfers=3, transferSize=maxPacketSize)\nStart polling the endpoint.\n\nThe library will keep `nTransfers` transfers of size `transferSize` pending in\nthe kernel at all times to ensure continuous data flow. This is handled by the\nlibusb event thread, so it continues even if the Node v8 thread is busy. The\n`data` and `error` events are emitted as transfers complete.\n\n#### .stopPoll(cb)\nStop polling.\n\nFurther data may still be received. The `end` event is emitted and the callback\nis called once all transfers have completed or canceled.\n\n#### Event: data(data : Buffer)\nEmitted with data received by the polling transfers\n\n#### Event: error(error)\nEmitted when polling encounters an error. All in flight transfers will be automatically canceled and no further polling will be done. You have to wait for the `end` event before you can start polling again.\n\n#### Event: end\nEmitted when polling has been canceled\n\n### OutEndpoint\nEndpoints in the OUT direction (PC-\u003edevice) have this type.\n\n#### .transfer(data, callback(error))\nPerform a transfer to write `data` to the endpoint.\n\nIf length is greater than maxPacketSize, libusb will automatically split the transfer in multiple packets, and you will receive one callback once all packets are complete.\n\n`this` in the callback is the OutEndpoint object.\n\n#### Event: error(error)\nEmitted when the stream encounters an error.\n\n#### Event: end\nEmitted when the stream has been stopped and all pending requests have been completed.\n\n### UsbDetection\n\n#### usb.on('attach', function(device) { ... });\nAttaches a callback to plugging in a `device`.\n\n#### usb.on('detach', function(device) { ... });\nAttaches a callback to unplugging a `device`.\n\n#### usb.refHotplugEvents();\nRestore (re-reference) the hotplug events unreferenced by `unrefHotplugEvents()`\n\n#### usb.unrefHotplugEvents();\nListening to events will prevent the process to exit. By calling this function, hotplug events will be unreferenced by the event loop, allowing the process to exit even when listening for the `attach` and `detach` events.\n\n# Migrating from node-usb-detection\n\nIf you have been referred here by `node-usb-detection`, the following may be helpful for you to update your existing code.\n\n## usbDetect.startMonitoring() \u0026 usbDetect.stopMonitoring()\n\nThere is no direct equivalent to these methods. This is handled automatically for you when you add and remove event listeners.\n\nYou may find `usb.unrefHotplugEvents()` useful as it is intended to help with exit conditions.\n\n## usbDetect.find()\n\nYou can instead use `usb.getDeviceList()`. Be aware that this will do an enumeration to find all of the devices, and not look at a cache of the known devices. If you call it too often it may have a performance impact.\n\nTo find a specific device by vid and pid, call `usb.findByIds`. e.g. `usb.findByIds(0x12, 0x34)`.\n\n## usbDetect.on('add', function(device) { ... })\n\nThese should be changed to `usb.on('attach', function(device) { ... })`.\n\nThere is no equivalent to filter based on the vid or pid, instead you should do a check inside the callback you provide.  \nThe contents of the device object has also changed.\n\n## usbDetect.on('remove', function(device) { ... })\n\nThese should be changed to `usb.on('detach', function(device) { ... })`.\n\nThere is no equivalent to filter based on the vid or pid, instead you should do a check inside the callback you provide.  \nThe contents of the device object has also changed.\n\n## usbDetect.on('change', function(device) { ... })\n\nThere is no direct equivalent to this. Instead you can listen to both `attach` and `detach` to get the same behaviour.\n\n# Development\nThe library is based on native bindings wrapping the [libusb](https://github.com/libusb/libusb) library.\n\n## Setup\nLibusb is included as a submodule, clone this repository and then the submodule as follows:\n\n```bash\ngit clone https://github.com/node-usb/node-usb\ncd node-usb\ngit submodule update --init\n```\n\nAlternatively, if you want to build this library against your system `libusb` set the `use_system_libusb` variable to `true` in the [binding.gyp](./binding.gyp) and [libusb.gypi](./libusb.gypi) files.\n\n## Building\nThe package uses `prebuildify` to generate the native binaries using an `install` script and `TypeScript` for the binding code using the `compile` script. The package can be built as follows:\n\n```bash\nyarn\nyarn compile\n```\n\nThe native binaries can be rebuilt with:\n\n```bash\nyarn rebuild\n```\n\n__Note:__ On Linux, you'll need libudev to build libusb. On Ubuntu/Debian:\n\n```bash\nsudo apt-get install build-essential libudev-dev\n```\n\n## Testing\nTo execute the unit tests, Run:\n\n```bash\nyarn test\n```\n\nSome tests require an [attached STM32F103 Microprocessor USB device with specific firmware](https://github.com/node-usb/node-usb-test-firmware).\n\n```bash\nyarn full-test\nyarn valgrind\n```\n\n## Releasing\nPlease refer to the [Wiki](https://github.com/node-usb/node-usb/wiki/Release-Process) for release instructions.\n","funding_links":[],"categories":["Packages","TypeScript","包"],"sub_categories":["Hardware","硬件"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-usb%2Fnode-usb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnode-usb%2Fnode-usb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnode-usb%2Fnode-usb/lists"}