smartcard 2.0.0 → 2.0.1

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,42 +1,108 @@
1
1
  # smartcard
2
2
 
3
- Modern PC/SC (Personal Computer/Smart Card) bindings for Node.js using N-API.
3
+ ```
4
+ ╭────────────────────────────────────────╮
5
+ │ │
6
+ │ ╭───────────╮ │
7
+ │ │ ▄▄▄ ▄▄▄▄▄ │ │
8
+ │ │ ███ ▄▄▄▄▄ │ │
9
+ │ │ ▀▀▀ ▀▀▀▀▀ │ │
10
+ │ ╰───────────╯ │
11
+ │ │
12
+ │ ░░░░░░░░░░░░ │
13
+ │ ░░░░░░░░░░░░ │
14
+ ╰────────────────────────────────────────╯
15
+ ```
4
16
 
5
- Unlike older NAN-based bindings that break with each Node.js major version, this library uses N-API for ABI stability across Node.js versions 12, 14, 16, 18, 20, 22, 24, and beyond - without recompilation.
17
+ Stable PC/SC smart card bindings for Node.js.
6
18
 
7
- ## Features
19
+ Works with Node.js 12+ without recompilation. Built on N-API for long-term stability.
8
20
 
9
- - **ABI Stable**: Works across Node.js versions without recompilation
10
- - **Async/Promise-based**: Non-blocking card operations
11
- - **Event-driven API**: High-level `Devices` class with EventEmitter
12
- - **TypeScript support**: Full type definitions included
13
- - **Cross-platform**: Windows, macOS, and Linux
21
+ ## Getting Started
14
22
 
15
- ## Installation
23
+ ### 1. Install the package
16
24
 
17
25
  ```bash
18
26
  npm install smartcard
19
27
  ```
20
28
 
21
- ### Prerequisites
22
-
23
- **macOS**: No additional setup required (uses built-in PCSC.framework)
29
+ ### 2. Platform setup
24
30
 
25
- **Windows**: No additional setup required (uses built-in winscard.dll)
31
+ **macOS/Windows**: Ready to go - no additional setup needed.
26
32
 
27
33
  **Linux**:
28
34
  ```bash
29
- # Debian/Ubuntu
30
- sudo apt-get install libpcsclite-dev pcscd
35
+ # Install PC/SC libraries
36
+ sudo apt-get install libpcsclite-dev pcscd # Debian/Ubuntu
37
+ sudo dnf install pcsc-lite-devel pcsc-lite # Fedora/RHEL
31
38
 
32
- # Fedora/RHEL
33
- sudo dnf install pcsc-lite-devel pcsc-lite
34
-
35
- # Start the PC/SC daemon
39
+ # Start the daemon
36
40
  sudo systemctl start pcscd
37
41
  ```
38
42
 
39
- ## Quick Start
43
+ ### 3. Connect a reader and run your first script
44
+
45
+ ```javascript
46
+ const { Devices } = require('smartcard');
47
+
48
+ const devices = new Devices();
49
+
50
+ devices.on('card-inserted', async ({ reader, card }) => {
51
+ console.log(`Card detected in ${reader.name}`);
52
+ console.log(`ATR: ${card.atr.toString('hex')}`);
53
+
54
+ // Get card UID (works with most contactless cards)
55
+ const response = await card.transmit([0xFF, 0xCA, 0x00, 0x00, 0x00]);
56
+ console.log(`UID: ${response.slice(0, -2).toString('hex')}`);
57
+ });
58
+
59
+ devices.on('error', (err) => console.error(err.message));
60
+
61
+ devices.start();
62
+ ```
63
+
64
+ Run it:
65
+ ```bash
66
+ node app.js
67
+ # Tap a card on your reader...
68
+ # Card detected in ACS ACR122U
69
+ # ATR: 3b8f8001804f0ca0000003060300030000000068
70
+ # UID: 04a23b7a
71
+ ```
72
+
73
+ ## Recommended Hardware
74
+
75
+ ### Readers
76
+
77
+ | Reader | Type | Notes |
78
+ |--------|------|-------|
79
+ | **ACR122U** | USB contactless | Affordable, widely available. Great for getting started. |
80
+ | **ACR1252U** | USB dual-interface | Supports both contactless and contact cards. |
81
+ | **SCM SCR35xx** | USB contact | Tested with SCR35xx v2.0. Good for contact smart cards. |
82
+ | **HID Omnikey 5427** | USB contactless | Enterprise-grade, faster reads. |
83
+ | **Identiv uTrust 3700F** | USB contactless | Compact, reliable. |
84
+
85
+ Any PC/SC compatible reader should work. The library uses standard PC/SC APIs.
86
+
87
+ ### Cards
88
+
89
+ | Card Type | Interface | Notes |
90
+ |-----------|-----------|-------|
91
+ | MIFARE Classic 1K/4K | Contactless | Most common NFC cards |
92
+ | MIFARE Ultralight / NTAG | Contactless | Stickers, wristbands, keyfobs |
93
+ | MIFARE DESFire | Contactless | Higher security applications |
94
+ | ISO 14443-4 | Contactless | Generic contactless smart cards |
95
+ | ISO 7816 | Contact | Standard contact smart cards (SIM, bank cards, ID cards) |
96
+
97
+ ## Features
98
+
99
+ - **ABI Stable**: Works across Node.js versions without recompilation
100
+ - **Async/Promise-based**: Non-blocking card operations
101
+ - **Event-driven API**: High-level `Devices` class with EventEmitter
102
+ - **TypeScript support**: Full type definitions included
103
+ - **Cross-platform**: Windows, macOS, and Linux
104
+
105
+ ## More Examples
40
106
 
41
107
  ### High-Level API (Event-Driven)
42
108
 
package/lib/devices.js CHANGED
@@ -1,14 +1,29 @@
1
+ // @ts-check
1
2
  'use strict';
2
3
 
3
4
  const EventEmitter = require('events');
4
5
  const addon = require('../build/Release/smartcard_napi.node');
5
6
 
7
+ /**
8
+ * @typedef {import('./index').Reader} Reader
9
+ * @typedef {import('./index').Card} Card
10
+ * @typedef {import('./index').MonitorEvent} MonitorEvent
11
+ * @typedef {import('./index').Context} ContextType
12
+ * @typedef {import('./index').ReaderMonitor} ReaderMonitorType
13
+ */
14
+
6
15
  const { Context, ReaderMonitor } = addon;
7
16
  const SCARD_STATE_PRESENT = addon.SCARD_STATE_PRESENT;
8
17
  const SCARD_SHARE_SHARED = addon.SCARD_SHARE_SHARED;
9
18
  const SCARD_PROTOCOL_T0 = addon.SCARD_PROTOCOL_T0;
10
19
  const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
11
20
 
21
+ /**
22
+ * @typedef {Object} ReaderState
23
+ * @property {boolean} hasCard
24
+ * @property {Card|null} card
25
+ */
26
+
12
27
  /**
13
28
  * High-level event-driven API for PC/SC devices
14
29
  *
@@ -21,14 +36,20 @@ const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
21
36
  * - 'card-inserted': Emitted when a card is inserted
22
37
  * - 'card-removed': Emitted when a card is removed
23
38
  * - 'error': Emitted on errors
39
+ *
40
+ * @extends EventEmitter
24
41
  */
25
42
  class Devices extends EventEmitter {
26
43
  constructor() {
27
44
  super();
45
+ /** @type {ReaderMonitorType|null} */
28
46
  this._monitor = null;
47
+ /** @type {ContextType|null} */
29
48
  this._context = null;
49
+ /** @type {boolean} */
30
50
  this._running = false;
31
- this._readers = new Map(); // name -> { hasCard, card }
51
+ /** @type {Map<string, ReaderState>} */
52
+ this._readers = new Map();
32
53
  }
33
54
 
34
55
  /**
@@ -95,7 +116,7 @@ class Devices extends EventEmitter {
95
116
 
96
117
  /**
97
118
  * List currently known readers
98
- * @returns {Array} Array of reader names
119
+ * @returns {Reader[]} Array of readers
99
120
  */
100
121
  listReaders() {
101
122
  if (!this._context || !this._context.isValid) {
@@ -110,6 +131,8 @@ class Devices extends EventEmitter {
110
131
 
111
132
  /**
112
133
  * Handle events from native monitor
134
+ * @param {MonitorEvent} event
135
+ * @returns {Promise<void>}
113
136
  */
114
137
  async _handleEvent(event) {
115
138
  if (!this._running) {
@@ -144,6 +167,9 @@ class Devices extends EventEmitter {
144
167
 
145
168
  /**
146
169
  * Handle reader attached
170
+ * @param {string} readerName
171
+ * @param {number} state
172
+ * @param {Buffer|null} atr
147
173
  */
148
174
  _handleReaderAttached(readerName, state, atr) {
149
175
  // Initialize reader state
@@ -169,6 +195,7 @@ class Devices extends EventEmitter {
169
195
 
170
196
  /**
171
197
  * Handle reader detached
198
+ * @param {string} readerName
172
199
  */
173
200
  _handleReaderDetached(readerName) {
174
201
  const state = this._readers.get(readerName);
@@ -186,6 +213,10 @@ class Devices extends EventEmitter {
186
213
 
187
214
  /**
188
215
  * Handle card inserted
216
+ * @param {string} readerName
217
+ * @param {number} eventState
218
+ * @param {Buffer|null} atr
219
+ * @returns {Promise<void>}
189
220
  */
190
221
  async _handleCardInserted(readerName, eventState, atr) {
191
222
  let state = this._readers.get(readerName);
@@ -222,6 +253,7 @@ class Devices extends EventEmitter {
222
253
 
223
254
  /**
224
255
  * Handle card removed
256
+ * @param {string} readerName
225
257
  */
226
258
  _handleCardRemoved(readerName) {
227
259
  const state = this._readers.get(readerName);
package/lib/errors.js CHANGED
@@ -1,9 +1,14 @@
1
+ // @ts-check
1
2
  'use strict';
2
3
 
3
4
  /**
4
5
  * Base error class for PC/SC errors
5
6
  */
6
7
  class PCSCError extends Error {
8
+ /**
9
+ * @param {string} message
10
+ * @param {number} code
11
+ */
7
12
  constructor(message, code) {
8
13
  super(message);
9
14
  this.name = 'PCSCError';
package/lib/index.js CHANGED
@@ -1,7 +1,6 @@
1
+ // @ts-check
1
2
  'use strict';
2
3
 
3
- const path = require('path');
4
-
5
4
  // Load native addon
6
5
  const addon = require('../build/Release/smartcard_napi.node');
7
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartcard",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "PC/SC bindings for Node.js using N-API - ABI stable across Node.js versions",
5
5
  "author": "Tom KP",
6
6
  "license": "MIT",
@@ -136,6 +136,14 @@ void ReaderMonitor::MonitorLoop() {
136
136
  // Get initial reader list
137
137
  UpdateReaderList();
138
138
 
139
+ // Emit reader-attached events for all pre-existing readers (Issue #30)
140
+ {
141
+ std::lock_guard<std::mutex> lock(mutex_);
142
+ for (const auto& reader : readers_) {
143
+ EmitEvent("reader-attached", reader.name, reader.lastState, reader.atr);
144
+ }
145
+ }
146
+
139
147
  // Build initial states array with PnP notification
140
148
  std::vector<SCARD_READERSTATE> states;
141
149
  std::vector<std::string> readerNames;