@mcesystems/usb-device-listener 1.0.14 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ import type { DeviceInfo, ListenerConfig } from "./types";
2
+ /**
3
+ * Determine if a device notification should be sent based on the configuration.
4
+ *
5
+ * Filter priority (highest to lowest):
6
+ * 1. ignoredDevices - if device matches, always return false
7
+ * 2. listenOnlyDevices - if specified, device must match at least one
8
+ * 3. targetDevices - if specified, device must match at least one
9
+ * 4. logicalPortMap - if specified, device location must be in the map
10
+ *
11
+ * @param device - The device information from the native addon
12
+ * @param config - The listener configuration
13
+ * @returns true if the device should trigger a notification, false otherwise
14
+ */
15
+ export declare function shouldNotifyDevice(device: DeviceInfo, config: ListenerConfig): boolean;
16
+ /**
17
+ * Apply logical port mapping to a device if configured
18
+ *
19
+ * @param device - The device information from the native addon
20
+ * @param config - The listener configuration
21
+ * @returns Device info with logicalPort set if mapped, otherwise unchanged
22
+ */
23
+ export declare function applyLogicalPortMapping(device: DeviceInfo, config: ListenerConfig): DeviceInfo;
24
+ //# sourceMappingURL=device-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-filter.d.ts","sourceRoot":"","sources":["../src/device-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAgB,MAAM,SAAS,CAAC;AA4BxE;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CA8BtF;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,cAAc,GAAG,UAAU,CAQ9F"}
@@ -0,0 +1,32 @@
1
+ import type { DeviceAddCallback, DeviceInfo, DeviceRemoveCallback, ListenerConfig, UsbDeviceListenerI } from "./types";
2
+ /**
3
+ * USB Device Listener implementation
4
+ * Provides a type-safe wrapper around the native C++ addon
5
+ */
6
+ export default class UsbDeviceListener implements UsbDeviceListenerI {
7
+ private config;
8
+ private userAddCallback;
9
+ private userRemoveCallback;
10
+ /**
11
+ * Start listening for USB device events
12
+ */
13
+ startListening(config: ListenerConfig): void;
14
+ /**
15
+ * Stop listening for USB device events
16
+ */
17
+ stopListening(): void;
18
+ /**
19
+ * Register callback for device connection events
20
+ */
21
+ onDeviceAdd(callback: DeviceAddCallback): void;
22
+ /**
23
+ * Register callback for device disconnection events
24
+ */
25
+ onDeviceRemove(callback: DeviceRemoveCallback): void;
26
+ /**
27
+ * List all currently connected USB devices
28
+ */
29
+ listDevices(): DeviceInfo[];
30
+ }
31
+ export type { DeviceInfo, DeviceAddCallback, DeviceRemoveCallback, ListenerConfig, TargetDevice, } from "./types";
32
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACX,iBAAiB,EACjB,UAAU,EACV,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,MAAM,SAAS,CAAC;AAoBjB;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAkB,YAAW,kBAAkB;IACnE,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,kBAAkB,CAAqC;IAE/D;;OAEG;IACI,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAQnD;;OAEG;IACI,aAAa,IAAI,IAAI;IAI5B;;OAEG;IACI,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAerD;;OAEG;IACI,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI;IAe3D;;OAEG;IACI,WAAW,IAAI,UAAU,EAAE;CAGlC;AAED,YAAY,EACX,UAAU,EACV,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,YAAY,GACZ,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,120 @@
1
+ // src/index.ts
2
+ import { createRequire } from "node:module";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ // src/device-filter.ts
7
+ function toHexString(value) {
8
+ return value.toString(16).toUpperCase().padStart(4, "0");
9
+ }
10
+ function matchesDevice(device, target) {
11
+ const deviceVid = toHexString(device.vid);
12
+ const devicePid = toHexString(device.pid);
13
+ const targetVid = target.vid.toUpperCase();
14
+ const targetPid = target.pid.toUpperCase();
15
+ return deviceVid === targetVid && devicePid === targetPid;
16
+ }
17
+ function matchesAnyDevice(device, targets) {
18
+ return targets.some((target) => matchesDevice(device, target));
19
+ }
20
+ function shouldNotifyDevice(device, config) {
21
+ if (config.ignoredDevices && config.ignoredDevices.length > 0) {
22
+ if (matchesAnyDevice(device, config.ignoredDevices)) {
23
+ return false;
24
+ }
25
+ }
26
+ if (config.listenOnlyDevices && config.listenOnlyDevices.length > 0) {
27
+ if (!matchesAnyDevice(device, config.listenOnlyDevices)) {
28
+ return false;
29
+ }
30
+ }
31
+ if (config.targetDevices && config.targetDevices.length > 0) {
32
+ if (!matchesAnyDevice(device, config.targetDevices)) {
33
+ return false;
34
+ }
35
+ }
36
+ if (config.logicalPortMap && Object.keys(config.logicalPortMap).length > 0) {
37
+ if (!(device.locationInfo in config.logicalPortMap)) {
38
+ return false;
39
+ }
40
+ }
41
+ return true;
42
+ }
43
+ function applyLogicalPortMapping(device, config) {
44
+ if (config.logicalPortMap && device.locationInfo in config.logicalPortMap) {
45
+ return {
46
+ ...device,
47
+ logicalPort: config.logicalPortMap[device.locationInfo]
48
+ };
49
+ }
50
+ return device;
51
+ }
52
+
53
+ // src/index.ts
54
+ var __filename = fileURLToPath(import.meta.url);
55
+ var __dirname = path.dirname(__filename);
56
+ var require2 = createRequire(import.meta.url);
57
+ var packageRoot = path.join(__dirname, "..");
58
+ var addon = require2("node-gyp-build")(packageRoot);
59
+ var { listDevices, onDeviceAdd, onDeviceRemove, startListening, stopListening } = addon;
60
+ var UsbDeviceListener = class {
61
+ config = {};
62
+ userAddCallback = null;
63
+ userRemoveCallback = null;
64
+ /**
65
+ * Start listening for USB device events
66
+ */
67
+ startListening(config) {
68
+ if (typeof config !== "object" || config === null) {
69
+ throw new TypeError("Config must be an object");
70
+ }
71
+ this.config = config;
72
+ startListening();
73
+ }
74
+ /**
75
+ * Stop listening for USB device events
76
+ */
77
+ stopListening() {
78
+ stopListening();
79
+ }
80
+ /**
81
+ * Register callback for device connection events
82
+ */
83
+ onDeviceAdd(callback) {
84
+ if (typeof callback !== "function") {
85
+ throw new TypeError("Callback must be a function");
86
+ }
87
+ this.userAddCallback = callback;
88
+ onDeviceAdd((device) => {
89
+ if (shouldNotifyDevice(device, this.config)) {
90
+ const enrichedDevice = applyLogicalPortMapping(device, this.config);
91
+ this.userAddCallback?.(enrichedDevice);
92
+ }
93
+ });
94
+ }
95
+ /**
96
+ * Register callback for device disconnection events
97
+ */
98
+ onDeviceRemove(callback) {
99
+ if (typeof callback !== "function") {
100
+ throw new TypeError("Callback must be a function");
101
+ }
102
+ this.userRemoveCallback = callback;
103
+ onDeviceRemove((device) => {
104
+ if (shouldNotifyDevice(device, this.config)) {
105
+ const enrichedDevice = applyLogicalPortMapping(device, this.config);
106
+ this.userRemoveCallback?.(enrichedDevice);
107
+ }
108
+ });
109
+ }
110
+ /**
111
+ * List all currently connected USB devices
112
+ */
113
+ listDevices() {
114
+ return listDevices();
115
+ }
116
+ };
117
+ export {
118
+ UsbDeviceListener as default
119
+ };
120
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/device-filter.ts"],
4
+ "sourcesContent": ["import { createRequire } from \"node:module\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\nimport { applyLogicalPortMapping, shouldNotifyDevice } from \"./device-filter\";\r\nimport type {\r\n\tDeviceAddCallback,\r\n\tDeviceInfo,\r\n\tDeviceRemoveCallback,\r\n\tListenerConfig,\r\n\tUsbDeviceListenerI,\r\n} from \"./types\";\r\n\r\ninterface NativeAddon {\r\n\tstartListening(): void;\r\n\tstopListening(): void;\r\n\tonDeviceAdd(callback: DeviceAddCallback): void;\r\n\tonDeviceRemove(callback: DeviceRemoveCallback): void;\r\n\tlistDevices(): DeviceInfo[];\r\n}\r\n\r\nconst __filename = fileURLToPath(import.meta.url);\r\nconst __dirname = path.dirname(__filename);\r\n\r\n// Load native addon using require (CJS) from ESM context\r\nconst require = createRequire(import.meta.url);\r\nconst packageRoot = path.join(__dirname, \"..\");\r\nconst addon: NativeAddon = require(\"node-gyp-build\")(packageRoot);\r\n\r\nconst { listDevices, onDeviceAdd, onDeviceRemove, startListening, stopListening } = addon;\r\n\r\n/**\r\n * USB Device Listener implementation\r\n * Provides a type-safe wrapper around the native C++ addon\r\n */\r\nexport default class UsbDeviceListener implements UsbDeviceListenerI {\r\n\tprivate config: ListenerConfig = {};\r\n\tprivate userAddCallback: DeviceAddCallback | null = null;\r\n\tprivate userRemoveCallback: DeviceRemoveCallback | null = null;\r\n\r\n\t/**\r\n\t * Start listening for USB device events\r\n\t */\r\n\tpublic startListening(config: ListenerConfig): void {\r\n\t\tif (typeof config !== \"object\" || config === null) {\r\n\t\t\tthrow new TypeError(\"Config must be an object\");\r\n\t\t}\r\n\t\tthis.config = config;\r\n\t\tstartListening();\r\n\t}\r\n\r\n\t/**\r\n\t * Stop listening for USB device events\r\n\t */\r\n\tpublic stopListening(): void {\r\n\t\tstopListening();\r\n\t}\r\n\r\n\t/**\r\n\t * Register callback for device connection events\r\n\t */\r\n\tpublic onDeviceAdd(callback: DeviceAddCallback): void {\r\n\t\tif (typeof callback !== \"function\") {\r\n\t\t\tthrow new TypeError(\"Callback must be a function\");\r\n\t\t}\r\n\t\tthis.userAddCallback = callback;\r\n\r\n\t\t// Set up internal callback that filters devices\r\n\t\tonDeviceAdd((device: DeviceInfo) => {\r\n\t\t\tif (shouldNotifyDevice(device, this.config)) {\r\n\t\t\t\tconst enrichedDevice = applyLogicalPortMapping(device, this.config);\r\n\t\t\t\tthis.userAddCallback?.(enrichedDevice);\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Register callback for device disconnection events\r\n\t */\r\n\tpublic onDeviceRemove(callback: DeviceRemoveCallback): void {\r\n\t\tif (typeof callback !== \"function\") {\r\n\t\t\tthrow new TypeError(\"Callback must be a function\");\r\n\t\t}\r\n\t\tthis.userRemoveCallback = callback;\r\n\r\n\t\t// Set up internal callback that filters devices\r\n\t\tonDeviceRemove((device: DeviceInfo) => {\r\n\t\t\tif (shouldNotifyDevice(device, this.config)) {\r\n\t\t\t\tconst enrichedDevice = applyLogicalPortMapping(device, this.config);\r\n\t\t\t\tthis.userRemoveCallback?.(enrichedDevice);\r\n\t\t\t}\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * List all currently connected USB devices\r\n\t */\r\n\tpublic listDevices(): DeviceInfo[] {\r\n\t\treturn listDevices();\r\n\t}\r\n}\r\n\r\nexport type {\r\n\tDeviceInfo,\r\n\tDeviceAddCallback,\r\n\tDeviceRemoveCallback,\r\n\tListenerConfig,\r\n\tTargetDevice,\r\n} from \"./types\";\r\n", "import type { DeviceInfo, ListenerConfig, TargetDevice } from \"./types\";\n\n/**\n * Convert a decimal VID/PID to uppercase hex string for comparison\n */\nfunction toHexString(value: number): string {\n\treturn value.toString(16).toUpperCase().padStart(4, \"0\");\n}\n\n/**\n * Check if a device matches a target device filter by VID/PID\n */\nfunction matchesDevice(device: DeviceInfo, target: TargetDevice): boolean {\n\tconst deviceVid = toHexString(device.vid);\n\tconst devicePid = toHexString(device.pid);\n\tconst targetVid = target.vid.toUpperCase();\n\tconst targetPid = target.pid.toUpperCase();\n\n\treturn deviceVid === targetVid && devicePid === targetPid;\n}\n\n/**\n * Check if a device matches any device in a list of target devices\n */\nfunction matchesAnyDevice(device: DeviceInfo, targets: TargetDevice[]): boolean {\n\treturn targets.some((target) => matchesDevice(device, target));\n}\n\n/**\n * Determine if a device notification should be sent based on the configuration.\n *\n * Filter priority (highest to lowest):\n * 1. ignoredDevices - if device matches, always return false\n * 2. listenOnlyDevices - if specified, device must match at least one\n * 3. targetDevices - if specified, device must match at least one\n * 4. logicalPortMap - if specified, device location must be in the map\n *\n * @param device - The device information from the native addon\n * @param config - The listener configuration\n * @returns true if the device should trigger a notification, false otherwise\n */\nexport function shouldNotifyDevice(device: DeviceInfo, config: ListenerConfig): boolean {\n\t// Priority 1: Check ignoredDevices (highest priority - always blocks)\n\tif (config.ignoredDevices && config.ignoredDevices.length > 0) {\n\t\tif (matchesAnyDevice(device, config.ignoredDevices)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Priority 2: Check listenOnlyDevices (if specified, device must match)\n\tif (config.listenOnlyDevices && config.listenOnlyDevices.length > 0) {\n\t\tif (!matchesAnyDevice(device, config.listenOnlyDevices)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Priority 3: Check targetDevices (if specified, device must match)\n\tif (config.targetDevices && config.targetDevices.length > 0) {\n\t\tif (!matchesAnyDevice(device, config.targetDevices)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// Priority 4: Check logicalPortMap (if specified, device must be mapped)\n\tif (config.logicalPortMap && Object.keys(config.logicalPortMap).length > 0) {\n\t\tif (!(device.locationInfo in config.logicalPortMap)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Apply logical port mapping to a device if configured\n *\n * @param device - The device information from the native addon\n * @param config - The listener configuration\n * @returns Device info with logicalPort set if mapped, otherwise unchanged\n */\nexport function applyLogicalPortMapping(device: DeviceInfo, config: ListenerConfig): DeviceInfo {\n\tif (config.logicalPortMap && device.locationInfo in config.logicalPortMap) {\n\t\treturn {\n\t\t\t...device,\n\t\t\tlogicalPort: config.logicalPortMap[device.locationInfo],\n\t\t};\n\t}\n\treturn device;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["require"]
7
+ }
@@ -0,0 +1,195 @@
1
+ /**
2
+ * USB Device Listener Type Definitions
3
+ *
4
+ * Type-safe interface for monitoring USB device connections and disconnections
5
+ */
6
+ /**
7
+ * Device information returned by the listener
8
+ */
9
+ export interface DeviceInfo {
10
+ /**
11
+ * Windows device instance ID
12
+ * Format: "USB\VID_xxxx&PID_xxxx\..."
13
+ */
14
+ deviceId: string;
15
+ /**
16
+ * USB Vendor ID (decimal)
17
+ * Use .toString(16) to convert to hex string
18
+ */
19
+ vid: number;
20
+ /**
21
+ * USB Product ID (decimal)
22
+ * Use .toString(16) to convert to hex string
23
+ */
24
+ pid: number;
25
+ /**
26
+ * Physical USB port location
27
+ * Format: "Port_#xxxx.Hub_#yyyy"
28
+ * This value is stable and can be used as a key for logical port mapping
29
+ */
30
+ locationInfo: string;
31
+ /**
32
+ * User-defined logical port number from configuration
33
+ * null if not mapped in logicalPortMap
34
+ */
35
+ logicalPort: number | null;
36
+ }
37
+ /**
38
+ * Target device filter by VID/PID
39
+ */
40
+ export interface TargetDevice {
41
+ /**
42
+ * USB Vendor ID as hex string (e.g., "04E8")
43
+ */
44
+ vid: string;
45
+ /**
46
+ * USB Product ID as hex string (e.g., "6860")
47
+ */
48
+ pid: string;
49
+ }
50
+ /**
51
+ * Configuration for USB device listener
52
+ */
53
+ export interface ListenerConfig {
54
+ /**
55
+ * Map physical port locations to logical port numbers
56
+ *
57
+ * Key: Location info string (e.g., "Port_#0005.Hub_#0002")
58
+ * Value: Logical port number (e.g., 1, 2, 3)
59
+ *
60
+ * Use listDevices() to discover location strings for your setup
61
+ *
62
+ * @example
63
+ * {
64
+ * "Port_#0005.Hub_#0002": 1,
65
+ * "Port_#0006.Hub_#0002": 2
66
+ * }
67
+ */
68
+ logicalPortMap?: Record<string, number>;
69
+ /**
70
+ * Filter to monitor only specific devices by VID/PID
71
+ *
72
+ * Empty array or undefined = monitor all USB devices
73
+ *
74
+ * @example
75
+ * [
76
+ * { vid: "04E8", pid: "6860" }, // Samsung device
77
+ * { vid: "2341", pid: "0043" } // Arduino Uno
78
+ * ]
79
+ */
80
+ targetDevices?: TargetDevice[];
81
+ /**
82
+ * Devices to ignore by VID/PID
83
+ *
84
+ * Devices matching any entry will not trigger events,
85
+ * even if they match other filters.
86
+ * This takes highest priority over all other filters.
87
+ *
88
+ * @example
89
+ * [
90
+ * { vid: "1234", pid: "5678" } // Ignore this specific device
91
+ * ]
92
+ */
93
+ ignoredDevices?: TargetDevice[];
94
+ /**
95
+ * Only monitor these specific devices by VID/PID
96
+ *
97
+ * Empty array or undefined = monitor all devices (subject to other filters)
98
+ * When set, only devices matching an entry will trigger events.
99
+ *
100
+ * @example
101
+ * [
102
+ * { vid: "04E8", pid: "6860" } // Only listen to this device
103
+ * ]
104
+ */
105
+ listenOnlyDevices?: TargetDevice[];
106
+ }
107
+ /**
108
+ * Callback type for device add events
109
+ */
110
+ export type DeviceAddCallback = (deviceInfo: DeviceInfo) => void;
111
+ /**
112
+ * Callback type for device remove events
113
+ */
114
+ export type DeviceRemoveCallback = (deviceInfo: DeviceInfo) => void;
115
+ /**
116
+ * USB Device Listener Module
117
+ */
118
+ export interface UsbDeviceListenerI {
119
+ /**
120
+ * Start listening for USB device events
121
+ *
122
+ * Creates a background thread that monitors Windows device change messages.
123
+ * Callbacks registered with onDeviceAdd/onDeviceRemove will be invoked
124
+ * when matching devices are connected or disconnected.
125
+ *
126
+ * @param config - Listener configuration
127
+ * @throws {TypeError} If config is not an object
128
+ * @throws {Error} If listener is already running
129
+ *
130
+ * @example
131
+ * listener.startListening({
132
+ * logicalPortMap: { "Port_#0005.Hub_#0002": 1 },
133
+ * targetDevices: [{ vid: "04E8", pid: "6860" }]
134
+ * });
135
+ */
136
+ startListening(config: ListenerConfig): void;
137
+ /**
138
+ * Stop listening for USB device events
139
+ *
140
+ * Stops the background thread and cleans up resources.
141
+ * Safe to call multiple times.
142
+ *
143
+ * @example
144
+ * listener.stopListening();
145
+ */
146
+ stopListening(): void;
147
+ /**
148
+ * Register callback for device connection events
149
+ *
150
+ * The callback will be invoked on the Node.js main thread whenever
151
+ * a matching USB device is connected.
152
+ *
153
+ * @param callback - Function to call when device is connected
154
+ * @throws {TypeError} If callback is not a function
155
+ *
156
+ * @example
157
+ * listener.onDeviceAdd((device) => {
158
+ * console.log(`Device ${device.vid.toString(16)}:${device.pid.toString(16)}`);
159
+ * console.log(`Connected to port ${device.logicalPort}`);
160
+ * });
161
+ */
162
+ onDeviceAdd(callback: DeviceAddCallback): void;
163
+ /**
164
+ * Register callback for device disconnection events
165
+ *
166
+ * The callback will be invoked on the Node.js main thread whenever
167
+ * a matching USB device is disconnected.
168
+ *
169
+ * @param callback - Function to call when device is disconnected
170
+ * @throws {TypeError} If callback is not a function
171
+ *
172
+ * @example
173
+ * listener.onDeviceRemove((device) => {
174
+ * console.log(`Port ${device.logicalPort} disconnected`);
175
+ * });
176
+ */
177
+ onDeviceRemove(callback: DeviceRemoveCallback): void;
178
+ /**
179
+ * List all currently connected USB devices
180
+ *
181
+ * Returns information about all USB devices currently connected to the system.
182
+ * Useful for discovering device locations and initial state.
183
+ *
184
+ * @returns Array of device information objects
185
+ *
186
+ * @example
187
+ * const devices = listener.listDevices();
188
+ * devices.forEach(device => {
189
+ * console.log(`Location: ${device.locationInfo}`);
190
+ * console.log(`VID:PID = ${device.vid.toString(16)}:${device.pid.toString(16)}`);
191
+ * });
192
+ */
193
+ listDevices(): DeviceInfo[];
194
+ }
195
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAE/B;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,YAAY,EAAE,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAE7C;;;;;;;;OAQG;IACH,aAAa,IAAI,IAAI,CAAC;IAEtB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE/C;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAErD;;;;;;;;;;;;;;OAcG;IACH,WAAW,IAAI,UAAU,EAAE,CAAC;CAC5B"}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@mcesystems/usb-device-listener",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Native Windows USB device listener using PnP notifications without custom drivers",
5
5
  "type": "module",
6
- "main": "src/index.ts",
7
- "types": "src/index.ts",
6
+ "main": "dist/index.js",
7
+ "types": "dist/types.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./src/index.ts",
11
- "types": "./src/index.ts"
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/types.d.ts"
12
12
  }
13
13
  },
14
14
  "keywords": [
@@ -49,19 +49,19 @@
49
49
  "vitest": "^2.1.8"
50
50
  },
51
51
  "files": [
52
- "src",
52
+ "dist",
53
53
  "prebuilds",
54
+ "build/Release/*.node",
55
+ "README.md",
54
56
  "native",
55
- "binding.gyp",
56
- "tsconfig.json",
57
- "README.md"
57
+ "binding.gyp"
58
58
  ],
59
59
  "gypfile": true,
60
60
  "os": [
61
61
  "win32"
62
62
  ],
63
63
  "scripts": {
64
- "prebuild:gyp": "prebuildify --electron --strip --target 25.2.1",
64
+ "prebuild:gyp": "prebuildify --napi --electron --strip --target 25.2.1",
65
65
  "build": "tsx esbuild.config.ts && tsc --emitDeclarationOnly",
66
66
  "rebuild": "node-gyp rebuild",
67
67
  "install": "node-gyp-build",
@@ -1,89 +0,0 @@
1
- import type { DeviceInfo, ListenerConfig, TargetDevice } from "./types";
2
-
3
- /**
4
- * Convert a decimal VID/PID to uppercase hex string for comparison
5
- */
6
- function toHexString(value: number): string {
7
- return value.toString(16).toUpperCase().padStart(4, "0");
8
- }
9
-
10
- /**
11
- * Check if a device matches a target device filter by VID/PID
12
- */
13
- function matchesDevice(device: DeviceInfo, target: TargetDevice): boolean {
14
- const deviceVid = toHexString(device.vid);
15
- const devicePid = toHexString(device.pid);
16
- const targetVid = target.vid.toUpperCase();
17
- const targetPid = target.pid.toUpperCase();
18
-
19
- return deviceVid === targetVid && devicePid === targetPid;
20
- }
21
-
22
- /**
23
- * Check if a device matches any device in a list of target devices
24
- */
25
- function matchesAnyDevice(device: DeviceInfo, targets: TargetDevice[]): boolean {
26
- return targets.some((target) => matchesDevice(device, target));
27
- }
28
-
29
- /**
30
- * Determine if a device notification should be sent based on the configuration.
31
- *
32
- * Filter priority (highest to lowest):
33
- * 1. ignoredDevices - if device matches, always return false
34
- * 2. listenOnlyDevices - if specified, device must match at least one
35
- * 3. targetDevices - if specified, device must match at least one
36
- * 4. logicalPortMap - if specified, device location must be in the map
37
- *
38
- * @param device - The device information from the native addon
39
- * @param config - The listener configuration
40
- * @returns true if the device should trigger a notification, false otherwise
41
- */
42
- export function shouldNotifyDevice(device: DeviceInfo, config: ListenerConfig): boolean {
43
- // Priority 1: Check ignoredDevices (highest priority - always blocks)
44
- if (config.ignoredDevices && config.ignoredDevices.length > 0) {
45
- if (matchesAnyDevice(device, config.ignoredDevices)) {
46
- return false;
47
- }
48
- }
49
-
50
- // Priority 2: Check listenOnlyDevices (if specified, device must match)
51
- if (config.listenOnlyDevices && config.listenOnlyDevices.length > 0) {
52
- if (!matchesAnyDevice(device, config.listenOnlyDevices)) {
53
- return false;
54
- }
55
- }
56
-
57
- // Priority 3: Check targetDevices (if specified, device must match)
58
- if (config.targetDevices && config.targetDevices.length > 0) {
59
- if (!matchesAnyDevice(device, config.targetDevices)) {
60
- return false;
61
- }
62
- }
63
-
64
- // Priority 4: Check logicalPortMap (if specified, device must be mapped)
65
- if (config.logicalPortMap && Object.keys(config.logicalPortMap).length > 0) {
66
- if (!(device.locationInfo in config.logicalPortMap)) {
67
- return false;
68
- }
69
- }
70
-
71
- return true;
72
- }
73
-
74
- /**
75
- * Apply logical port mapping to a device if configured
76
- *
77
- * @param device - The device information from the native addon
78
- * @param config - The listener configuration
79
- * @returns Device info with logicalPort set if mapped, otherwise unchanged
80
- */
81
- export function applyLogicalPortMapping(device: DeviceInfo, config: ListenerConfig): DeviceInfo {
82
- if (config.logicalPortMap && device.locationInfo in config.logicalPortMap) {
83
- return {
84
- ...device,
85
- logicalPort: config.logicalPortMap[device.locationInfo],
86
- };
87
- }
88
- return device;
89
- }
@@ -1,60 +0,0 @@
1
- import usbListener, { type DeviceInfo, type ListenerConfig } from "../index";
2
-
3
- // Type-safe configuration - no casting needed!
4
- const config: ListenerConfig = {
5
- logicalPortMap: {
6
- "Port_#0005.Hub_#0002": 1,
7
- "Port_#0006.Hub_#0002": 2,
8
- },
9
- ignoredDevices: [],
10
- listenOnlyDevices: [],
11
- };
12
-
13
- // Type-safe callback with full IntelliSense
14
- usbListener.onDeviceAdd((device: DeviceInfo) => {
15
- console.log("Device connected:");
16
- console.log(" Device ID:", device.deviceId);
17
- console.log(` VID: 0x${device.vid.toString(16).toUpperCase().padStart(4, "0")}`);
18
- console.log(` PID: 0x${device.pid.toString(16).toUpperCase().padStart(4, "0")}`);
19
- console.log(" Location:", device.locationInfo);
20
- console.log(" Logical Port:", device.logicalPort !== null ? device.logicalPort : "Not mapped");
21
- console.log("");
22
- });
23
-
24
- usbListener.onDeviceRemove((device: DeviceInfo) => {
25
- console.log("Device disconnected:");
26
- console.log(" Device ID:", device.deviceId);
27
- console.log(` VID: 0x${device.vid.toString(16).toUpperCase().padStart(4, "0")}`);
28
- console.log(` PID: 0x${device.pid.toString(16).toUpperCase().padStart(4, "0")}`);
29
- console.log(" Location:", device.locationInfo);
30
- console.log(" Logical Port:", device.logicalPort !== null ? device.logicalPort : "Not mapped");
31
- console.log("");
32
- });
33
-
34
- try {
35
- console.log("Starting USB device listener...");
36
- console.log("Config:", JSON.stringify(config, null, 2));
37
- console.log("");
38
-
39
- usbListener.startListening(config);
40
-
41
- console.log("Listening for USB device events. Press Ctrl+C to stop.");
42
- console.log("");
43
- } catch (error) {
44
- if (error instanceof Error) {
45
- console.error("Error starting listener:", error.message);
46
- }
47
- process.exit(1);
48
- }
49
-
50
- process.on("SIGINT", () => {
51
- console.log("\nStopping listener...");
52
- usbListener.stopListening();
53
- process.exit(0);
54
- });
55
-
56
- process.on("SIGTERM", () => {
57
- console.log("\nStopping listener...");
58
- usbListener.stopListening();
59
- process.exit(0);
60
- });
@@ -1,29 +0,0 @@
1
- import usbListener, { type DeviceInfo } from "../index";
2
-
3
- console.log("Listing all connected USB devices:\n");
4
-
5
- try {
6
- const devices: DeviceInfo[] = usbListener.listDevices();
7
-
8
- if (devices.length === 0) {
9
- console.log("No USB devices found.");
10
- } else {
11
- devices.forEach((device: DeviceInfo, index: number) => {
12
- console.log(`Device ${index + 1}:`);
13
- console.log(` Device ID: ${device.deviceId}`);
14
- console.log(` VID: 0x${device.vid.toString(16).toUpperCase().padStart(4, "0")}`);
15
- console.log(` PID: 0x${device.pid.toString(16).toUpperCase().padStart(4, "0")}`);
16
- console.log(` Location Info (mapping key): ${device.locationInfo}`);
17
- console.log("");
18
- });
19
-
20
- console.log(`Total devices: ${devices.length}`);
21
- console.log(
22
- '\nTo add a device to your config, use the "Location Info" as the key in the logicalPortMap.'
23
- );
24
- }
25
- } catch (error) {
26
- if (error instanceof Error) {
27
- console.error("Error listing devices:", error.message);
28
- }
29
- }
package/src/index.ts DELETED
@@ -1,144 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import { dirname } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { applyLogicalPortMapping, shouldNotifyDevice } from "./device-filter";
6
- import type {
7
- DeviceAddCallback,
8
- DeviceInfo,
9
- DeviceRemoveCallback,
10
- ListenerConfig,
11
- UsbDeviceListener,
12
- } from "./types";
13
-
14
- /**
15
- * Native addon interface
16
- * This is the raw C++ addon loaded via N-API
17
- */
18
- interface NativeAddon {
19
- startListening(): void;
20
- stopListening(): void;
21
- onDeviceAdd(callback: DeviceAddCallback): void;
22
- onDeviceRemove(callback: DeviceRemoveCallback): void;
23
- listDevices(): DeviceInfo[];
24
- }
25
-
26
- // ESM compatibility: get __dirname equivalent
27
- const __filename = fileURLToPath(import.meta.url);
28
- const __dirname = dirname(__filename);
29
-
30
- const packageRoot = dirname(__dirname);
31
-
32
- /**
33
- * Load native addon
34
- * In development: uses node-gyp-build to find the addon
35
- * In production: loads directly from the known build location
36
- */
37
- function loadNativeAddon(): NativeAddon {
38
- const require = createRequire(import.meta.url);
39
- return require("node-gyp-build")(packageRoot);
40
- }
41
- let addon: NativeAddon;
42
- try {
43
- if (
44
- process.env.USB_DEVICE_LISTENER_ADDON_PATH &&
45
- existsSync(process.env.USB_DEVICE_LISTENER_ADDON_PATH)
46
- ) {
47
- console.log("Loading native addon from direct path", process.env.USB_DEVICE_LISTENER_ADDON_PATH);
48
- addon = require(process.env.USB_DEVICE_LISTENER_ADDON_PATH);
49
- } else {
50
- console.log("Loading native addon from node-gyp-build");
51
- addon = loadNativeAddon();
52
- }
53
- } catch (_error) {
54
- throw new Error(
55
- `Failed to load native addon. Tried:\n 1. Direct path: ${packageRoot}\n 2. node-gyp-build search\nMake sure the addon is built with 'npm run rebuild' or 'npm run prebuild'`
56
- );
57
- }
58
- if (!addon) {
59
- throw new Error("Failed to load native addon");
60
- }
61
-
62
- /**
63
- * USB Device Listener implementation
64
- * Provides a type-safe wrapper around the native C++ addon
65
- */
66
- class UsbDeviceListenerImpl implements UsbDeviceListener {
67
- private config: ListenerConfig = {};
68
- private userAddCallback: DeviceAddCallback | null = null;
69
- private userRemoveCallback: DeviceRemoveCallback | null = null;
70
-
71
- /**
72
- * Start listening for USB device events
73
- */
74
- public startListening(config: ListenerConfig): void {
75
- if (typeof config !== "object" || config === null) {
76
- throw new TypeError("Config must be an object");
77
- }
78
- this.config = config;
79
- addon.startListening();
80
- }
81
-
82
- /**
83
- * Stop listening for USB device events
84
- */
85
- public stopListening(): void {
86
- addon.stopListening();
87
- }
88
-
89
- /**
90
- * Register callback for device connection events
91
- */
92
- public onDeviceAdd(callback: DeviceAddCallback): void {
93
- if (typeof callback !== "function") {
94
- throw new TypeError("Callback must be a function");
95
- }
96
- this.userAddCallback = callback;
97
-
98
- // Set up internal callback that filters devices
99
- addon.onDeviceAdd((device: DeviceInfo) => {
100
- if (shouldNotifyDevice(device, this.config)) {
101
- const enrichedDevice = applyLogicalPortMapping(device, this.config);
102
- this.userAddCallback?.(enrichedDevice);
103
- }
104
- });
105
- }
106
-
107
- /**
108
- * Register callback for device disconnection events
109
- */
110
- public onDeviceRemove(callback: DeviceRemoveCallback): void {
111
- if (typeof callback !== "function") {
112
- throw new TypeError("Callback must be a function");
113
- }
114
- this.userRemoveCallback = callback;
115
-
116
- // Set up internal callback that filters devices
117
- addon.onDeviceRemove((device: DeviceInfo) => {
118
- if (shouldNotifyDevice(device, this.config)) {
119
- const enrichedDevice = applyLogicalPortMapping(device, this.config);
120
- this.userRemoveCallback?.(enrichedDevice);
121
- }
122
- });
123
- }
124
-
125
- /**
126
- * List all currently connected USB devices
127
- */
128
- public listDevices(): DeviceInfo[] {
129
- return addon.listDevices();
130
- }
131
- }
132
-
133
- // Export singleton instance
134
- const usbDeviceListener: UsbDeviceListener = new UsbDeviceListenerImpl();
135
-
136
- export default usbDeviceListener;
137
- export type {
138
- DeviceInfo,
139
- DeviceAddCallback,
140
- DeviceRemoveCallback,
141
- ListenerConfig,
142
- TargetDevice,
143
- UsbDeviceListener,
144
- } from "./types";
package/src/types.ts DELETED
@@ -1,212 +0,0 @@
1
- /**
2
- * USB Device Listener Type Definitions
3
- *
4
- * Type-safe interface for monitoring USB device connections and disconnections
5
- */
6
-
7
- /**
8
- * Device information returned by the listener
9
- */
10
- export interface DeviceInfo {
11
- /**
12
- * Windows device instance ID
13
- * Format: "USB\VID_xxxx&PID_xxxx\..."
14
- */
15
- deviceId: string;
16
-
17
- /**
18
- * USB Vendor ID (decimal)
19
- * Use .toString(16) to convert to hex string
20
- */
21
- vid: number;
22
-
23
- /**
24
- * USB Product ID (decimal)
25
- * Use .toString(16) to convert to hex string
26
- */
27
- pid: number;
28
-
29
- /**
30
- * Physical USB port location
31
- * Format: "Port_#xxxx.Hub_#yyyy"
32
- * This value is stable and can be used as a key for logical port mapping
33
- */
34
- locationInfo: string;
35
-
36
- /**
37
- * User-defined logical port number from configuration
38
- * null if not mapped in logicalPortMap
39
- */
40
- logicalPort: number | null;
41
- }
42
-
43
- /**
44
- * Target device filter by VID/PID
45
- */
46
- export interface TargetDevice {
47
- /**
48
- * USB Vendor ID as hex string (e.g., "04E8")
49
- */
50
- vid: string;
51
-
52
- /**
53
- * USB Product ID as hex string (e.g., "6860")
54
- */
55
- pid: string;
56
- }
57
-
58
- /**
59
- * Configuration for USB device listener
60
- */
61
- export interface ListenerConfig {
62
- /**
63
- * Map physical port locations to logical port numbers
64
- *
65
- * Key: Location info string (e.g., "Port_#0005.Hub_#0002")
66
- * Value: Logical port number (e.g., 1, 2, 3)
67
- *
68
- * Use listDevices() to discover location strings for your setup
69
- *
70
- * @example
71
- * {
72
- * "Port_#0005.Hub_#0002": 1,
73
- * "Port_#0006.Hub_#0002": 2
74
- * }
75
- */
76
- logicalPortMap?: Record<string, number>;
77
-
78
- /**
79
- * Filter to monitor only specific devices by VID/PID
80
- *
81
- * Empty array or undefined = monitor all USB devices
82
- *
83
- * @example
84
- * [
85
- * { vid: "04E8", pid: "6860" }, // Samsung device
86
- * { vid: "2341", pid: "0043" } // Arduino Uno
87
- * ]
88
- */
89
- targetDevices?: TargetDevice[];
90
-
91
- /**
92
- * Devices to ignore by VID/PID
93
- *
94
- * Devices matching any entry will not trigger events,
95
- * even if they match other filters.
96
- * This takes highest priority over all other filters.
97
- *
98
- * @example
99
- * [
100
- * { vid: "1234", pid: "5678" } // Ignore this specific device
101
- * ]
102
- */
103
- ignoredDevices?: TargetDevice[];
104
-
105
- /**
106
- * Only monitor these specific devices by VID/PID
107
- *
108
- * Empty array or undefined = monitor all devices (subject to other filters)
109
- * When set, only devices matching an entry will trigger events.
110
- *
111
- * @example
112
- * [
113
- * { vid: "04E8", pid: "6860" } // Only listen to this device
114
- * ]
115
- */
116
- listenOnlyDevices?: TargetDevice[];
117
- }
118
-
119
- /**
120
- * Callback type for device add events
121
- */
122
- export type DeviceAddCallback = (deviceInfo: DeviceInfo) => void;
123
-
124
- /**
125
- * Callback type for device remove events
126
- */
127
- export type DeviceRemoveCallback = (deviceInfo: DeviceInfo) => void;
128
-
129
- /**
130
- * USB Device Listener Module
131
- */
132
- export interface UsbDeviceListener {
133
- /**
134
- * Start listening for USB device events
135
- *
136
- * Creates a background thread that monitors Windows device change messages.
137
- * Callbacks registered with onDeviceAdd/onDeviceRemove will be invoked
138
- * when matching devices are connected or disconnected.
139
- *
140
- * @param config - Listener configuration
141
- * @throws {TypeError} If config is not an object
142
- * @throws {Error} If listener is already running
143
- *
144
- * @example
145
- * listener.startListening({
146
- * logicalPortMap: { "Port_#0005.Hub_#0002": 1 },
147
- * targetDevices: [{ vid: "04E8", pid: "6860" }]
148
- * });
149
- */
150
- startListening(config: ListenerConfig): void;
151
-
152
- /**
153
- * Stop listening for USB device events
154
- *
155
- * Stops the background thread and cleans up resources.
156
- * Safe to call multiple times.
157
- *
158
- * @example
159
- * listener.stopListening();
160
- */
161
- stopListening(): void;
162
-
163
- /**
164
- * Register callback for device connection events
165
- *
166
- * The callback will be invoked on the Node.js main thread whenever
167
- * a matching USB device is connected.
168
- *
169
- * @param callback - Function to call when device is connected
170
- * @throws {TypeError} If callback is not a function
171
- *
172
- * @example
173
- * listener.onDeviceAdd((device) => {
174
- * console.log(`Device ${device.vid.toString(16)}:${device.pid.toString(16)}`);
175
- * console.log(`Connected to port ${device.logicalPort}`);
176
- * });
177
- */
178
- onDeviceAdd(callback: DeviceAddCallback): void;
179
-
180
- /**
181
- * Register callback for device disconnection events
182
- *
183
- * The callback will be invoked on the Node.js main thread whenever
184
- * a matching USB device is disconnected.
185
- *
186
- * @param callback - Function to call when device is disconnected
187
- * @throws {TypeError} If callback is not a function
188
- *
189
- * @example
190
- * listener.onDeviceRemove((device) => {
191
- * console.log(`Port ${device.logicalPort} disconnected`);
192
- * });
193
- */
194
- onDeviceRemove(callback: DeviceRemoveCallback): void;
195
-
196
- /**
197
- * List all currently connected USB devices
198
- *
199
- * Returns information about all USB devices currently connected to the system.
200
- * Useful for discovering device locations and initial state.
201
- *
202
- * @returns Array of device information objects
203
- *
204
- * @example
205
- * const devices = listener.listDevices();
206
- * devices.forEach(device => {
207
- * console.log(`Location: ${device.locationInfo}`);
208
- * console.log(`VID:PID = ${device.vid.toString(16)}:${device.pid.toString(16)}`);
209
- * });
210
- */
211
- listDevices(): DeviceInfo[];
212
- }
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "module": "ESNext",
7
- "moduleResolution": "bundler",
8
- "declaration": true,
9
- "declarationDir": "./dist",
10
- "typeRoots": ["./node_modules/@types"]
11
- },
12
- "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.d.cts"],
13
- "exclude": ["node_modules", "dist", "build", "test", "src/examples", "*.config.ts"]
14
- }