keyspy 1.0.3 → 1.0.5

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.
package/README.md CHANGED
@@ -1,19 +1,27 @@
1
- # KeySpy 🕵️
1
+ # keyspy 🕵️
2
2
 
3
3
  > A powerful, cross-platform keyboard and mouse event listener for Node.js
4
4
 
5
- KeySpy is a modern, lightweight library that provides global keyboard and mouse event monitoring across Windows, macOS, and Linux. Unlike other solutions, KeySpy uses pre-compiled native binaries and a multi-process architecture for maximum stability and compatibility.
5
+ keyspy is a modern, lightweight library that provides global keyboard and mouse event monitoring across Windows, macOS, and Linux. Unlike other solutions, keyspy uses pre-compiled native binaries and a multi-process architecture for maximum stability and compatibility.
6
+
7
+ ## 🚀 Improvements over Original
8
+
9
+ This project is a modernized version of [node-global-key-listener](https://github.com/LaunchMenu/node-global-key-listener) with significant enhancements:
10
+
11
+ - **🚀 Zero Setup**: Pre-compiled binaries downloaded automatically, no compilation required
12
+ - **📦 Smaller Package**: Binaries downloaded on-demand (not bundled), reducing package size by ~90%
13
+ - **📱 Universal macOS**: ARM64 + x86_64 universal binaries for all Apple Silicon and Intel Macs
14
+ - **🔄 Modern Stack**: TypeScript, automated testing, and modern development tools
15
+ - **🏗️ Automated Releases**: GitHub Actions handle cross-platform compilation and publishing
6
16
 
7
17
  ## ✨ Features
8
18
 
9
- - 🌍 **Cross-platform**: Works on Windows, macOS, and Linux (X11)
10
- - 🚀 **Zero compilation**: Pre-compiled binaries downloaded automatically, no node-gyp required
11
- - 🔒 **System-level capture**: Can intercept system shortcuts like Ctrl+Alt+Delete, Cmd+Space
19
+ - 🌍 **Cross-platform**: Windows, macOS, and Linux (X11) support
20
+ - 🔒 **System-level capture**: Intercept any key combination, including OS shortcuts
12
21
  - 🎯 **Event blocking**: Prevent captured events from reaching other applications
13
- - 📦 **TypeScript ready**: Full TypeScript support with comprehensive type definitions
14
- - 🏗️ **Modern architecture**: Multi-process design for enhanced stability
22
+ - 📦 **TypeScript ready**: Full type definitions included
23
+ - 🛡️ **Stable architecture**: Multi-process design prevents crashes
15
24
  - ⚡ **High performance**: Optimized native implementations for each platform
16
- - 📥 **Smart installation**: Automatically downloads platform-specific binaries from GitHub Releases
17
25
 
18
26
  ## 🔧 Platform Support
19
27
 
@@ -106,22 +114,16 @@ keyspy.kill(); // Removes all listeners and stops the key server
106
114
  ```bash
107
115
  npm install keyspy
108
116
  # or
109
- pnpm add keyspy
110
- # or
111
117
  yarn add keyspy
112
118
  ```
113
119
 
114
- **What happens during installation:**
115
- 1. 📦 Downloads the main package (TypeScript code)
116
- 2. 🔍 Detects your platform (Windows/macOS/Linux) and architecture
117
- 3. 📥 Automatically downloads the appropriate pre-compiled binary from GitHub Releases
118
- 4. ✅ Ready to use immediately!
120
+ The package automatically detects your platform and downloads the appropriate pre-compiled binary during installation. No compilation required!
119
121
 
120
- ## 🤔 Why KeySpy?
122
+ ## 🤔 Why keyspy?
121
123
 
122
- Choosing the right keyboard listener for your Node.js project can be challenging. Here's how KeySpy compares to other popular solutions:
124
+ Choosing the right keyboard listener for your Node.js project can be challenging. Here's how keyspy compares to other popular solutions:
123
125
 
124
- | Feature | Electron globalShortcut | IOHook | **KeySpy** |
126
+ | Feature | Electron globalShortcut | IOHook | **keyspy** |
125
127
  |---------|------------------------|--------|------------|
126
128
  | **Setup Complexity** | Simple | Complex (node-gyp) | **Simple** |
127
129
  | **System Shortcuts** | ❌ Limited | ✅ Full | **✅ Full** |
@@ -131,7 +133,7 @@ Choosing the right keyboard listener for your Node.js project can be challenging
131
133
  | **Arbitrary Key Support** | ❌ Limited | ⚠️ Limited | **✅ Full** |
132
134
  | **Process Architecture** | In-process | In-process | **Multi-process** |
133
135
 
134
- ### 🎯 **KeySpy Advantages**
136
+ ### 🎯 **keyspy Advantages**
135
137
 
136
138
  - **🔧 Zero Setup**: Pre-compiled binaries work out of the box
137
139
  - **🌐 Universal**: Compatible with all Node.js versions (14+)
@@ -148,47 +150,60 @@ Choosing the right keyboard listener for your Node.js project can be challenging
148
150
 
149
151
  ## 🛠️ Development
150
152
 
151
- ### Quick Development Setup
152
-
153
153
  ```bash
154
154
  # Clone and setup
155
155
  git clone https://github.com/teomyth/keyspy.git
156
156
  cd keyspy
157
- pnpm install
157
+ npm install
158
158
 
159
- # Development workflow
160
- pnpm dev # Watch mode compilation
161
- pnpm build # Production build
162
- pnpm test # Run all tests
163
- pnpm test:manual # Interactive testing
159
+ # Build native binary for development
160
+ npm run build:native
161
+
162
+ # Development commands
163
+ npm run dev # TypeScript watch mode
164
+ npm run build # Production build
165
+ npm test # Run all tests
166
+ npm run test:manual # Interactive testing
164
167
  ```
165
168
 
166
169
  ### Building Native Binaries
167
170
 
168
- KeySpy includes pre-compiled binaries, but you can rebuild them if needed:
171
+ For development, you can rebuild the native binaries:
169
172
 
170
173
  ```bash
171
- # Platform-specific builds
172
- pnpm build:win # Windows (requires MinGW)
173
- pnpm build:swift # macOS (requires Xcode)
174
- pnpm build:x11 # Linux (requires X11 dev libraries)
174
+ # Auto-detect your platform
175
+ npm run build:native
176
+
177
+ # Force specific platform
178
+ npm run build:native -- --platform darwin # macOS (requires Xcode)
179
+ npm run build:native -- --platform linux # Linux (requires X11 dev libraries)
180
+ npm run build:native -- --platform win32 # Windows (requires MinGW)
181
+
182
+ # Show detailed build output
183
+ npm run build:native -- --verbose
175
184
  ```
176
185
 
177
186
  ### Code Quality
178
187
 
179
188
  ```bash
180
- pnpm lint # Check code quality with Biome
181
- pnpm lint:fix # Auto-fix issues
182
- pnpm format # Format code
183
- pnpm clean # Remove build artifacts
189
+ npm run lint # Check code quality with Biome
190
+ npm run lint:fix # Auto-fix issues
191
+ npm run format # Format code
192
+ npm run clean # Remove build artifacts
193
+
194
+ # Release management
195
+ npm run release:patch # Bump patch version (1.0.0 → 1.0.1)
196
+ npm run release:minor # Bump minor version (1.0.0 → 1.1.0)
197
+ npm run release:major # Bump major version (1.0.0 → 2.0.0)
198
+ npm run release:dry # Preview release without publishing
184
199
  ```
185
200
 
186
201
  ### Testing
187
202
 
188
203
  ```bash
189
- pnpm test:unit # Unit tests
190
- pnpm test:integration # Integration tests
191
- pnpm test:manual # Manual interactive testing
204
+ npm run test:unit # Unit tests
205
+ npm run test:integration # Integration tests
206
+ npm run test:manual # Manual interactive testing
192
207
  ```
193
208
 
194
209
  ## 📋 API Reference
@@ -236,36 +251,17 @@ interface IGlobalKeyEvent {
236
251
 
237
252
  ## 🔧 Troubleshooting
238
253
 
239
- ### Binary Download Issues
240
-
241
- If the automatic binary download fails during installation:
242
-
243
- ```bash
244
- # Option 1: Manual download from GitHub Releases
245
- # Visit: https://github.com/teomyth/keyspy/releases
246
- # Download the appropriate file for your platform
247
-
248
- # Option 2: Build from source
249
- npm run build:swift # macOS
250
- npm run build:x11 # Linux
251
- npm run build:win # Windows
252
-
253
- # Option 3: Skip download in CI environments
254
- export KEYSPY_SKIP_DOWNLOAD=true
255
- npm install keyspy
256
- ```
257
-
258
- ### Platform Support
254
+ ### Common Issues
259
255
 
260
- - **macOS**: ARM64 (Apple Silicon) and x64 (Intel) - Universal binary
261
- - **Linux**: x64 only, requires X11
262
- - **Windows**: x64 only
256
+ 1. **Binary download fails**: Visit [GitHub Releases](https://github.com/teomyth/keyspy/releases) to download manually, or build from source with `npm run build:native`
257
+ 2. **Permission denied**: Make sure the binary has execute permissions (`chmod +x bin/*`)
258
+ 3. **CI environments**: Set `KEYSPY_SKIP_DOWNLOAD=true` to skip binary download
263
259
 
264
- ### Common Issues
260
+ ### Platform Requirements
265
261
 
266
- 1. **"Binary not found"**: Run the appropriate build command for your platform
267
- 2. **Permission denied**: Make sure the binary has execute permissions (`chmod +x`)
268
- 3. **Network issues**: Check your internet connection and GitHub access
262
+ - **macOS**: macOS 10.14+, supports both Intel and Apple Silicon
263
+ - **Linux**: X11 display server, x64 architecture
264
+ - **Windows**: Windows 10+, x64 architecture
269
265
 
270
266
  ## 🤝 Contributing
271
267
 
@@ -280,8 +276,3 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
280
276
  ## 📄 License
281
277
 
282
278
  MIT License - see [LICENSE](LICENSE) file for details.
283
-
284
- ## 🙏 Acknowledgments
285
-
286
- - Original concept inspired by [LaunchMenu](https://github.com/LaunchMenu/LaunchMenu)
287
- - Built with modern tooling: TypeScript, Biome, Jest, and Turbo
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keyspy",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A cross-platform global keyboard and mouse listener for Node.js",
5
5
  "types": "build/index.d.ts",
6
6
  "main": "build/index.js",
@@ -13,28 +13,29 @@
13
13
  "engines": {
14
14
  "node": ">=14.0.0"
15
15
  },
16
- "packageManager": "pnpm@8.0.0",
17
16
  "scripts": {
18
17
  "dev": "tsc --watch",
19
18
  "build": "tsc",
20
19
  "postinstall": "node scripts/download-binaries.js",
21
- "build:swift": "scripts/compile-swift.sh",
22
- "build:mac": "mkdir -p bin && swiftc src/bin/MacKeyServer/main.swift -o bin/MacKeyServer",
23
- "build:win": ".\\scripts\\compile-win.bat",
24
- "build:x11": "scripts/compile-x11.sh",
20
+ "build:native": "node scripts/build-native.js",
25
21
  "test": "jest",
26
22
  "test:unit": "jest tests/unit",
27
23
  "test:integration": "jest tests/integration",
24
+ "test:coverage": "jest --coverage",
25
+ "test:watch": "jest --watch",
28
26
  "test:manual": "npx ts-node examples/manual-test.ts",
29
27
  "lint": "biome lint .",
30
28
  "lint:fix": "biome lint --write .",
31
29
  "format": "biome format --write .",
32
30
  "format:check": "biome format .",
33
- "clean": "rm -rf build dist coverage test-reports .turbo",
31
+ "clean": "rm -rf build dist coverage test-results .turbo",
34
32
  "prepare": "npm run build",
35
33
  "prepublishOnly": "npm run build && npm run test",
36
34
  "release": "release-it",
37
- "release:dry": "release-it --dry-run"
35
+ "release:dry": "release-it --dry-run",
36
+ "release:patch": "release-it patch",
37
+ "release:minor": "release-it minor",
38
+ "release:major": "release-it major"
38
39
  },
39
40
  "repository": {
40
41
  "type": "git",
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const https = require('node:https');
6
- const { execSync } = require('node:child_process');
3
+ const fs = require("node:fs");
4
+ const path = require("node:path");
5
+ const https = require("node:https");
6
+ const { execSync } = require("node:child_process");
7
7
 
8
- const packageJson = require('../package.json');
8
+ const packageJson = require("../package.json");
9
9
  const version = packageJson.version;
10
10
 
11
11
  // Platform detection
@@ -14,16 +14,16 @@ const arch = process.arch;
14
14
 
15
15
  // Binary mapping
16
16
  const binaryMap = {
17
- 'darwin': {
18
- 'arm64': { file: 'MacKeyServer', archive: 'keyspy-darwin-arm64.tar.gz' },
19
- 'x64': { file: 'MacKeyServer', archive: 'keyspy-darwin-x64.tar.gz' }
17
+ darwin: {
18
+ arm64: { file: "MacKeyServer", archive: "keyspy-darwin-arm64.tar.gz" },
19
+ x64: { file: "MacKeyServer", archive: "keyspy-darwin-x64.tar.gz" },
20
20
  },
21
- 'linux': {
22
- 'x64': { file: 'X11KeyServer', archive: 'keyspy-linux-x64.tar.gz' }
21
+ linux: {
22
+ x64: { file: "X11KeyServer", archive: "keyspy-linux-x64.tar.gz" },
23
+ },
24
+ win32: {
25
+ x64: { file: "WinKeyServer.exe", archive: "keyspy-win32-x64.tar.gz" },
23
26
  },
24
- 'win32': {
25
- 'x64': { file: 'WinKeyServer.exe', archive: 'keyspy-win32-x64.tar.gz' }
26
- }
27
27
  };
28
28
 
29
29
  function log(message) {
@@ -39,56 +39,58 @@ function getPlatformInfo() {
39
39
  if (!platformInfo) {
40
40
  throw new Error(`Unsupported platform: ${platform}`);
41
41
  }
42
-
42
+
43
43
  const archInfo = platformInfo[arch];
44
44
  if (!archInfo) {
45
45
  throw new Error(`Unsupported architecture: ${arch} on ${platform}`);
46
46
  }
47
-
47
+
48
48
  return archInfo;
49
49
  }
50
50
 
51
51
  function downloadFile(url, dest) {
52
52
  return new Promise((resolve, reject) => {
53
53
  log(`Downloading ${url}`);
54
-
54
+
55
55
  const file = fs.createWriteStream(dest);
56
-
57
- https.get(url, (response) => {
58
- if (response.statusCode === 302 || response.statusCode === 301) {
59
- // Handle redirect
60
- return downloadFile(response.headers.location, dest).then(resolve).catch(reject);
61
- }
62
-
63
- if (response.statusCode !== 200) {
64
- reject(new Error(`Download failed with status ${response.statusCode}`));
65
- return;
66
- }
67
-
68
- response.pipe(file);
69
-
70
- file.on('finish', () => {
71
- file.close();
72
- resolve();
73
- });
74
-
75
- file.on('error', (err) => {
76
- fs.unlink(dest, () => {
77
- // Ignore unlink errors - file cleanup is best effort
78
- }); // Delete partial file
79
- reject(err);
80
- });
81
- }).on('error', reject);
56
+
57
+ https
58
+ .get(url, (response) => {
59
+ if (response.statusCode === 302 || response.statusCode === 301) {
60
+ // Handle redirect
61
+ return downloadFile(response.headers.location, dest).then(resolve).catch(reject);
62
+ }
63
+
64
+ if (response.statusCode !== 200) {
65
+ reject(new Error(`Download failed with status ${response.statusCode}`));
66
+ return;
67
+ }
68
+
69
+ response.pipe(file);
70
+
71
+ file.on("finish", () => {
72
+ file.close();
73
+ resolve();
74
+ });
75
+
76
+ file.on("error", (err) => {
77
+ fs.unlink(dest, () => {
78
+ // Ignore unlink errors - file cleanup is best effort
79
+ }); // Delete partial file
80
+ reject(err);
81
+ });
82
+ })
83
+ .on("error", reject);
82
84
  });
83
85
  }
84
86
 
85
87
  function extractTarGz(archivePath, extractDir) {
86
88
  try {
87
89
  // Try using tar command
88
- execSync(`tar -xzf "${archivePath}" -C "${extractDir}"`, { stdio: 'inherit' });
90
+ execSync(`tar -xzf "${archivePath}" -C "${extractDir}"`, { stdio: "inherit" });
89
91
  return true;
90
92
  } catch (_err) {
91
- log('tar command failed, trying alternative extraction...');
93
+ log("tar command failed, trying alternative extraction...");
92
94
  return false;
93
95
  }
94
96
  }
@@ -96,39 +98,39 @@ function extractTarGz(archivePath, extractDir) {
96
98
  async function downloadAndExtract() {
97
99
  try {
98
100
  const { file: binaryFile, archive } = getPlatformInfo();
99
-
101
+
100
102
  // Create bin directory
101
- const binDir = path.join(__dirname, '..', 'bin');
103
+ const binDir = path.join(__dirname, "..", "bin");
102
104
  if (!fs.existsSync(binDir)) {
103
105
  fs.mkdirSync(binDir, { recursive: true });
104
106
  }
105
-
107
+
106
108
  const binaryPath = path.join(binDir, binaryFile);
107
-
109
+
108
110
  // Check if binary already exists
109
111
  if (fs.existsSync(binaryPath)) {
110
112
  log(`Binary already exists: ${binaryPath}`);
111
113
  return;
112
114
  }
113
-
115
+
114
116
  // Download URL
115
117
  const downloadUrl = `https://github.com/teomyth/keyspy/releases/download/v${version}/${archive}`;
116
118
  const archivePath = path.join(binDir, archive);
117
-
119
+
118
120
  log(`Platform: ${platform}-${arch}`);
119
121
  log(`Binary: ${binaryFile}`);
120
-
122
+
121
123
  try {
122
124
  // Download archive
123
125
  await downloadFile(downloadUrl, archivePath);
124
126
  log(`Downloaded: ${archive}`);
125
-
127
+
126
128
  // Extract archive
127
129
  if (extractTarGz(archivePath, binDir)) {
128
130
  log(`Extracted: ${archive}`);
129
-
131
+
130
132
  // Make binary executable (Unix systems)
131
- if (platform !== 'win32') {
133
+ if (platform !== "win32") {
132
134
  try {
133
135
  fs.chmodSync(binaryPath, 0o755);
134
136
  log(`Made executable: ${binaryFile}`);
@@ -136,43 +138,41 @@ async function downloadAndExtract() {
136
138
  log(`Warning: Could not make binary executable: ${err.message}`);
137
139
  }
138
140
  }
139
-
141
+
140
142
  // Clean up archive
141
143
  fs.unlinkSync(archivePath);
142
144
  log(`Cleaned up: ${archive}`);
143
-
145
+
144
146
  log(`✅ Successfully installed binary: ${binaryFile}`);
145
147
  } else {
146
- throw new Error('Failed to extract archive');
148
+ throw new Error("Failed to extract archive");
147
149
  }
148
-
149
150
  } catch (downloadErr) {
150
151
  error(`Failed to download binary: ${downloadErr.message}`);
151
- log('');
152
- log('📝 Manual installation options:');
153
- log('1. Download from: https://github.com/teomyth/keyspy/releases');
154
- log('2. Build from source:');
155
- log(' - macOS: npm run build:swift');
156
- log(' - Linux: npm run build:x11');
157
- log(' - Windows: npm run build:win');
158
- log('');
159
- log('⚠️ KeySpy will not work without the platform-specific binary.');
160
-
152
+ log("");
153
+ log("📝 Manual installation options:");
154
+ log("1. Download from: https://github.com/teomyth/keyspy/releases");
155
+ log("2. Build from source:");
156
+ log(" - macOS: npm run build:swift");
157
+ log(" - Linux: npm run build:x11");
158
+ log(" - Windows: npm run build:win");
159
+ log("");
160
+ log("⚠️ keyspy will not work without the platform-specific binary.");
161
+
161
162
  // Don't fail the installation, just warn
162
163
  process.exit(0);
163
164
  }
164
-
165
165
  } catch (err) {
166
166
  error(err.message);
167
- log('');
168
- log('📋 Supported platforms:');
169
- log('- macOS (ARM64, x64)');
170
- log('- Linux (x64)');
171
- log('- Windows (x64)');
172
- log('');
173
- log('If your platform should be supported, please report this issue:');
174
- log('https://github.com/teomyth/keyspy/issues');
175
-
167
+ log("");
168
+ log("📋 Supported platforms:");
169
+ log("- macOS (ARM64, x64)");
170
+ log("- Linux (x64)");
171
+ log("- Windows (x64)");
172
+ log("");
173
+ log("If your platform should be supported, please report this issue:");
174
+ log("https://github.com/teomyth/keyspy/issues");
175
+
176
176
  // Don't fail the installation for unsupported platforms
177
177
  process.exit(0);
178
178
  }
@@ -180,7 +180,7 @@ async function downloadAndExtract() {
180
180
 
181
181
  // Skip download in CI environments or if explicitly disabled
182
182
  if (process.env.CI || process.env.KEYSPY_SKIP_DOWNLOAD) {
183
- log('Skipping binary download (CI environment or explicitly disabled)');
183
+ log("Skipping binary download (CI environment or explicitly disabled)");
184
184
  process.exit(0);
185
185
  }
186
186