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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions packages/native-machine-id/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,32 @@ npx native-machine-id --raw
### As a module

```javascript
import { getMachineID } from 'native-machine-id';
import { getMachineId } from 'native-machine-id';

// Get the machine ID, hashed with SHA-256
const hashedId = getMachineId();
console.log('Hashed Machine ID:', hashedId);

// Get the machine ID
const hashedId = getMachineID();
console.log('SHA-256 Hashed Machine ID:', hashedId);
const rawId = getMachineID({ raw: true });
// Get the raw machine ID (should not be exposed in untrusted environments)
const rawId = getMachineId({ raw: true });
console.log('Original Machine ID:', rawId);

// Or synchronously
import { getMachineIdSync } from 'native-machine-id';
const id = getMachineIdSync();
```

## Supported Platforms

- **macOS**: Uses the `IOPlatformUUID` from the `IOKit` framework (Supported on macOS 12.0 and later).
- **macOS**: Uses the `IOPlatformUUID` from the `IOKit` framework.
- **Linux**: Uses the `/var/lib/dbus/machine-id` file to retrieve the machine ID. If this file does not exist, it falls back to `/etc/machine-id`.
- **Windows**: Uses the `MachineGuid` from the `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography` registry.

## Comparison with `node-machine-id`

This module provides similar functionality to [node-machine-id](https://www.npmjs.com/package/node-machine-id) while **using native access to system APIs without the need for child processes**, making it much faster and reliable.

Here's a table of performance comparisons between the two libraries, based on the average runtime from 1000 iterations of the `getMachineId` and `machineIdSync` functions, from `scripts/benchmark.ts`:
Here's a table of performance comparisons between the two libraries, based on the average runtime from 1000 iterations of the `getMachineIdSync` and `machineIdSync` functions, from `scripts/benchmark.ts`:

| Test | node-machine-id | native-machine-id | Improvement |
| ----------- | --------------- | ----------------- | ----------- |
Expand All @@ -61,10 +67,10 @@ If you were previously using `node-machine-id`, you can use the following mappin

```ts
import { createHash } from 'crypto';
import { getMachineId } from 'native-machine-id';
import { getMachineIdSync } from 'native-machine-id';

function machineIdSync(original: boolean): string | undefined {
const rawMachineId = getMachineId({ raw: true }).toLowerCase();
const rawMachineId = getMachineIdSync({ raw: true }).toLowerCase();

return original
? rawMachineId
Expand Down
81 changes: 74 additions & 7 deletions packages/native-machine-id/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
// kIOMainPortDefault and kIOMasterPortDefault are both set to 0
// and we define them here to avoid conflicts across OS versions
#define IO_PORT 0
#elif defined(__linux__)
#include <fstream>
#include <algorithm>
Expand All @@ -21,7 +24,7 @@ namespace
std::string getMachineId() noexcept
{
std::string uuid;
io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(kIOMainPortDefault, "IOService:/");
io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(IO_PORT, "IOService:/");

if (ioRegistryRoot == MACH_PORT_NULL)
{
Expand Down Expand Up @@ -98,7 +101,7 @@ namespace
}

// Get Linux machine ID by reading from system files
std::string getMachineId()
std::string getMachineId() noexcept
{
std::string uuid = readFile(DBUS_PATH);

Expand All @@ -112,7 +115,7 @@ namespace
}
#elif defined(_WIN32)
// Get Windows machine ID from registry
std::string getMachineId()
std::string getMachineId() noexcept
{
std::string uuid;
HKEY hKey;
Expand Down Expand Up @@ -156,8 +159,8 @@ namespace
}
#endif

// Function to get the machine ID
Value GetMachineId(const CallbackInfo &args)
// Function to get the machine ID (synchronous version)
Value GetMachineIdSync(const CallbackInfo &args)
{
Env env = args.Env();

Expand All @@ -173,12 +176,76 @@ namespace
return env.Undefined();
}

// Async worker class for getting machine ID
class GetMachineIdWorker : public AsyncWorker
{
private:
std::string id;

public:
GetMachineIdWorker(Function &callback)
: AsyncWorker(callback) {}

// This runs on a worker thread
void Execute() override
{
#if defined(__APPLE__) || defined(__linux__) || defined(_WIN32)
id = getMachineId();
#endif
}

// This runs on the main thread after Execute completes
void OnOK() override
{
Napi::Env env = Env();
Napi::HandleScope scope(env);

if (!id.empty())
{
Callback().Call({env.Null(), String::New(env, id)});
}
else
{
Callback().Call({env.Null(), env.Undefined()});
}
}

void OnError(const Error &e) override
{
Napi::Env env = Env();
Napi::HandleScope scope(env);
Callback().Call({e.Value(), env.Undefined()});
}
};

// Function to get the machine ID asynchronously
Value GetMachineIdAsync(const CallbackInfo &args)
{
Env env = args.Env();

// Check if a callback was provided
if (args.Length() < 1 || !args[0].IsFunction())
{
TypeError::New(env, "Callback function expected").ThrowAsJavaScriptException();
return env.Undefined();
}

// Get the callback function
Function callback = args[0].As<Function>();

// Create and queue the async worker
GetMachineIdWorker *worker = new GetMachineIdWorker(callback);
worker->Queue();

return env.Undefined();
}
}

static Object InitModule(Env env, Object exports)
{
exports["getMachineId"] = Function::New(env, GetMachineId);
exports["getMachineIdSync"] = Function::New(env, GetMachineIdSync);
exports["getMachineIdAsync"] = Function::New(env, GetMachineIdAsync);
return exports;
}

NODE_API_MODULE(machine_id, InitModule)
NODE_API_MODULE(native_machine_id, InitModule)
2 changes: 1 addition & 1 deletion packages/native-machine-id/binding.gyp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
'targets': [{
'target_name': 'machine_id',
'target_name': 'native_machine_id',
'sources': [ 'binding.cc' ],
'include_dirs': ["<!(node -p \"require('node-addon-api').include_dir\")"],
'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],
Expand Down
8 changes: 5 additions & 3 deletions packages/native-machine-id/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "native-machine-id",
"version": "0.0.3",
"version": "0.0.8",
"description": "Native retrieval of a unique desktop machine ID without admin privileges or child processes. Faster and more reliable alternative to node-machine-id.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"compile": "tsc -p tsconfig.json && node-gyp rebuild && gen-esm-wrapper . ./dist/.esm-wrapper.mjs",
"bootstrap": "npm run compile",
"pretest": "npm run compile",
"install": "node-gyp rebuild",
"test": "mocha",
"test-cov": "nyc -x \"**/*.spec.*\" --reporter=lcov --reporter=text --reporter=html npm run test",
"test-ci": "npm run test-cov",
Expand All @@ -25,7 +26,8 @@
"license": "Apache-2.0",
"exports": {
"require": "./dist/index.js",
"import": "./dist/.esm-wrapper.mjs"
"import": "./dist/.esm-wrapper.mjs",
"types": "./dist/index.d.ts"
},
"homepage": "https://github.com/mongodb-js/devtools-shared",
"repository": {
Expand All @@ -34,7 +36,7 @@
},
"bugs": "https://jira.mongodb.org/projects/COMPASS/issues",
"bin": {
"machine-id": "dist/bin/machine-id.js"
"native-machine-id": "dist/bin/machine-id.js"
},
"files": [
"binding.cc",
Expand Down
2 changes: 1 addition & 1 deletion packages/native-machine-id/scripts/benchmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* against the node-machine-id package.
*/

import { getMachineId } from '../dist/index.js';
import { getMachineIdSync as getMachineId } from '../dist/index.js';
import { machineIdSync } from 'node-machine-id';

// Configuration
Expand Down
4 changes: 2 additions & 2 deletions packages/native-machine-id/src/bin/machine-id.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node
import { getMachineId } from '..';
import { getMachineIdSync } from '..';

const id = getMachineId({ raw: process.argv.includes('--raw') }) || '';
const id = getMachineIdSync({ raw: process.argv.includes('--raw') }) || '';

// eslint-disable-next-line no-console
console.log(id);
7 changes: 5 additions & 2 deletions packages/native-machine-id/src/bindings.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
declare module 'bindings' {
function bindings(filename: 'machine_id'): {
getMachineId: () => string | undefined;
function bindings(filename: 'native_machine_id'): {
getMachineIdSync: () => string | undefined;
getMachineIdAsync: (
callback: (err: Error | null, id: string | undefined) => void,
) => void;
};
export = bindings;
}
Loading
Loading