├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── src ├── exec.ts ├── index.ts ├── platforms │ ├── darwin.ts │ ├── linux.ts │ └── win32.ts └── resolve.ts ├── test └── hwid.test.ts ├── tsconfig.eslint.json ├── tsconfig.json ├── tsup.config.ts └── vitest.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.md] 10 | trim_trailing_whitespace = false 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "@luludev/eslint-config/common", 5 | "@luludev/eslint-config/node", 6 | "@luludev/eslint-config/typescript", 7 | "@luludev/eslint-config/prettier" 8 | ], 9 | "parserOptions": { 10 | "project": "./tsconfig.eslint.json" 11 | }, 12 | "rules": {}, 13 | "ignorePatterns": ["**/dist/*"] 14 | } 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # CRLF Normalisation 2 | * text eol=lf 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: JackBaron 2 | ko_fi: lolpants 3 | custom: 4 | - 'https://www.paypal.me/jackbarondev' 5 | - 'https://monzo.me/jackbaron' 6 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | if: contains(github.ref, 'refs/tags/') == false 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, windows-latest, macos-latest] 11 | node-version: [16.x, 18.x] 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v3 18 | with: 19 | cache: npm 20 | node-version: ${{ matrix.node-version }} 21 | - name: Install packages 22 | run: npm ci 23 | - name: Test 24 | run: npm run test 25 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Publish 2 | on: 3 | release: 4 | types: [published] 5 | 6 | env: 7 | NODE_VERSION: 18.x 8 | 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | id-token: write 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | - name: Setup Node.js 19 | uses: actions/setup-node@v3 20 | with: 21 | cache: npm 22 | node-version: ${{ env.NODE_VERSION }} 23 | registry-url: 'https://registry.npmjs.org' 24 | - name: Update npm 25 | run: npm install -g npm 26 | - name: Install packages 27 | run: npm ci 28 | - name: Publish to NPM 29 | run: npm publish --provenance --access public 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Docs 40 | docs 41 | 42 | # VSCode 43 | .vscode 44 | 45 | # Compiled TypeScript 46 | dist/ 47 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Generated 2 | dist/ 3 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | export default { 3 | semi: false, 4 | singleQuote: true, 5 | jsxSingleQuote: true, 6 | arrowParens: 'avoid', 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019-2023, Jack Baron 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 💻 HWID 2 | 3 | ![Node.js CI](https://github.com/luludotdev/hwid/workflows/Node.js%20CI/badge.svg?branch=master) 4 | [![NPM version](https://img.shields.io/npm/v/hwid.svg?maxAge=3600)](https://www.npmjs.com/package/hwid) 5 | [![NPM downloads](https://img.shields.io/npm/dt/hwid.svg?maxAge=3600)](https://www.npmjs.com/package/hwid) 6 | 7 | > Grab the system's unique hardware ID! 8 | 9 | ## 💾 Installation 10 | 11 | This package is published to the NPM registry as [`hwid`](https://www.npmjs.com/package/hwid). Install it with your NPM client of choice. 12 | 13 | ## 🔧 Usage 14 | 15 | First, import the module: 16 | 17 | ```js 18 | import { getHWID } from 'hwid' 19 | ``` 20 | 21 | From there, simply call the function. It returns a promise with the hardware ID as a string. As it returns a promise, you can also use it in an async/await context. 22 | 23 | ```js 24 | // Promises 25 | getHWID().then(id => { 26 | // use ID however you want 27 | }) 28 | 29 | // async/await 30 | async function main() { 31 | const id = await getHWID() 32 | // use ID however you want 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hwid", 3 | "version": "0.5.0", 4 | "type": "module", 5 | "main": "./dist/index.js", 6 | "module": "./dist/index.js", 7 | "types": "./dist/index.d.ts", 8 | "exports": { 9 | ".": { 10 | "import": "./dist/index.js", 11 | "types": "./dist/index.d.ts" 12 | } 13 | }, 14 | "sideEffects": false, 15 | "files": [ 16 | "dist" 17 | ], 18 | "scripts": { 19 | "build": "tsup", 20 | "dev": "npm run build -- --watch", 21 | "check": "tsc --noEmit --skipLibCheck", 22 | "lint": "prettier --check . && eslint src --ext ts", 23 | "format": "prettier --write . && eslint src --ext ts --fix", 24 | "fmt": "npm run format", 25 | "vitest": "vitest run", 26 | "test": "npm run check && npm run lint && npm run vitest", 27 | "prepack": "npm run test && npm run build" 28 | }, 29 | "author": "Jack Baron (https://www.jackbaron.com)", 30 | "license": "ISC", 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/luludotdev/hwid.git" 34 | }, 35 | "engines": { 36 | "node": ">=16.9.0" 37 | }, 38 | "devDependencies": { 39 | "@luludev/eslint-config": "^0.4.2", 40 | "@types/node": "^20.4.2", 41 | "@types/winreg": "^1.2.32", 42 | "@vitest/coverage-v8": "^0.33.0", 43 | "eslint": "^8.44.0", 44 | "prettier": "^3.0.0", 45 | "rimraf": "^5.0.1", 46 | "tsup": "^7.1.0", 47 | "typescript": "^5.1.6", 48 | "vitest": "^0.33.0" 49 | }, 50 | "dependencies": { 51 | "winreg": "^1.2.4" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/exec.ts: -------------------------------------------------------------------------------- 1 | import { exec as _exec } from 'node:child_process' 2 | import { promisify } from 'node:util' 3 | 4 | export const exec = promisify(_exec) 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { resolveID } from './resolve.js' 2 | 3 | export const getHWID = async () => { 4 | const hwid = await resolveID() 5 | if (hwid === '') throw new Error('failed to find hwid') 6 | 7 | return hwid 8 | } 9 | -------------------------------------------------------------------------------- /src/platforms/darwin.ts: -------------------------------------------------------------------------------- 1 | import { exec } from '../exec.js' 2 | import type { ResolverFn } from '../resolve.js' 3 | 4 | export const darwinHWID: ResolverFn = async () => { 5 | const { stdout } = await exec('ioreg -rd1 -c IOPlatformExpertDevice') 6 | const uuid = stdout 7 | .trim() 8 | .split('\n') 9 | .find(line => line.includes('IOPlatformUUID')) 10 | ?.replaceAll(/=|\s+|"/gi, '') 11 | .replaceAll('IOPlatformUUID', '') 12 | 13 | if (!uuid) throw new Error('failed to find hwid') 14 | return uuid 15 | } 16 | -------------------------------------------------------------------------------- /src/platforms/linux.ts: -------------------------------------------------------------------------------- 1 | import { exec } from '../exec.js' 2 | import type { ResolverFn } from '../resolve.js' 3 | 4 | export const linuxHWID: ResolverFn = async () => { 5 | const { stdout } = await exec( 6 | 'cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || true', 7 | ) 8 | 9 | const array = stdout.trim().split('\n') 10 | const first = array[0] 11 | if (!first) throw new Error('failed to find hwid') 12 | 13 | return first 14 | } 15 | -------------------------------------------------------------------------------- /src/platforms/win32.ts: -------------------------------------------------------------------------------- 1 | import { promisify } from 'node:util' 2 | import Registry from 'winreg' 3 | import type { ResolverFn } from '../resolve.js' 4 | 5 | export const win32HWID: ResolverFn = async () => { 6 | const regKey = new Registry({ 7 | hive: Registry.HKLM, 8 | key: '\\SOFTWARE\\Microsoft\\Cryptography', 9 | }) 10 | 11 | const getKey = promisify(regKey.get.bind(regKey)) 12 | const key = await getKey('MachineGuid') 13 | 14 | return key.value.toLowerCase() 15 | } 16 | -------------------------------------------------------------------------------- /src/resolve.ts: -------------------------------------------------------------------------------- 1 | import { platform } from 'node:process' 2 | import { darwinHWID } from './platforms/darwin.js' 3 | import { linuxHWID } from './platforms/linux.js' 4 | import { win32HWID } from './platforms/win32.js' 5 | 6 | export type ResolverFn = () => Promise 7 | 8 | export const resolveID: ResolverFn = async () => { 9 | switch (platform) { 10 | case 'win32': 11 | return win32HWID() 12 | 13 | case 'darwin': 14 | return darwinHWID() 15 | 16 | case 'linux': 17 | return linuxHWID() 18 | 19 | default: 20 | throw new Error('unsupported platform') 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/hwid.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest' 2 | import { getHWID } from '../src/index.js' 3 | 4 | test('is a function', () => { 5 | expect(typeof getHWID).eq('function') 6 | }) 7 | 8 | test('returns a string with length > 0', async () => { 9 | const hwid = await getHWID() 10 | expect(typeof hwid).eq('string') 11 | expect(hwid.length).greaterThan(0) 12 | }) 13 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true 5 | }, 6 | "include": [ 7 | "**/*.ts", 8 | "**/*.tsx", 9 | "**/*.js", 10 | "**/*.mjs", 11 | "**/*.jsx", 12 | "**/*.test.ts", 13 | "**/*.test.js", 14 | "**/*.test.mjs", 15 | "**/*.spec.ts", 16 | "**/*.spec.js", 17 | "**/*.spec.mjs" 18 | ], 19 | "exclude": [] 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | // Type Checking 5 | "allowUnreachableCode": false, 6 | "allowUnusedLabels": false, 7 | "exactOptionalPropertyTypes": true, 8 | "noFallthroughCasesInSwitch": true, 9 | "noImplicitOverride": true, 10 | "noImplicitReturns": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "strict": true, 14 | "useUnknownInCatchVariables": true, 15 | "noUncheckedIndexedAccess": true, 16 | 17 | // Modules 18 | "module": "ESNext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | 22 | // Emit 23 | "declaration": true, 24 | "declarationMap": true, 25 | "importHelpers": false, 26 | "noEmitHelpers": false, 27 | "inlineSources": true, 28 | "newLine": "lf", 29 | "outDir": "dist", 30 | "preserveConstEnums": true, 31 | "removeComments": false, 32 | "sourceMap": true, 33 | "esModuleInterop": true, 34 | "forceConsistentCasingInFileNames": true, 35 | 36 | // Language and Environment 37 | "emitDecoratorMetadata": false, 38 | "experimentalDecorators": false, 39 | "lib": ["ESNext"], 40 | "target": "ES2021", 41 | "useDefineForClassFields": true 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | import type { Options } from 'tsup' 3 | 4 | export function createTsupConfig({ 5 | entry = ['./src/index.ts'], 6 | external = [], 7 | noExternal = [], 8 | platform = 'node', 9 | format = 'esm', 10 | target = 'es2021', 11 | skipNodeModulesBundle = true, 12 | clean = true, 13 | shims = true, 14 | minify = false, 15 | splitting = false, 16 | keepNames = true, 17 | dts = true, 18 | sourcemap = true, 19 | }: Options = {}) { 20 | return defineConfig({ 21 | entry, 22 | external, 23 | noExternal, 24 | platform, 25 | format, 26 | skipNodeModulesBundle, 27 | target, 28 | clean, 29 | shims, 30 | minify, 31 | splitting, 32 | keepNames, 33 | dts, 34 | sourcemap, 35 | }) 36 | } 37 | 38 | export default createTsupConfig({ 39 | shims: false, 40 | }) 41 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config' 2 | 3 | export default defineConfig({ 4 | test: { 5 | exclude: ['**/node_modules', '**/dist', '.idea', '.git', '.cache'], 6 | passWithNoTests: true, 7 | coverage: { 8 | enabled: true, 9 | all: true, 10 | reporter: ['text', 'lcov', 'cobertura'], 11 | provider: 'v8', 12 | include: ['src'], 13 | exclude: [ 14 | // All ts files that only contain types, due to ALL 15 | '**/*.{interface,type,d}.ts', 16 | // All index files that *should* only contain exports from other files 17 | '**/index.{js,ts}', 18 | ], 19 | }, 20 | }, 21 | }) 22 | --------------------------------------------------------------------------------