smartcard 1.0.46 → 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/LICENSE +1 -1
- package/README.md +453 -0
- package/binding.gyp +45 -0
- package/lib/devices.js +283 -0
- package/lib/errors.js +77 -0
- package/lib/index.d.ts +247 -0
- package/lib/index.js +75 -14
- package/package.json +56 -39
- package/src/addon.cpp +54 -0
- package/src/async_workers.cpp +234 -0
- package/src/async_workers.h +100 -0
- package/src/pcsc_card.cpp +248 -0
- package/src/pcsc_card.h +41 -0
- package/src/pcsc_context.cpp +224 -0
- package/src/pcsc_context.h +32 -0
- package/src/pcsc_errors.h +49 -0
- package/src/pcsc_reader.cpp +89 -0
- package/src/pcsc_reader.h +39 -0
- package/src/platform/pcsc.h +36 -0
- package/src/reader_monitor.cpp +352 -0
- package/src/reader_monitor.h +57 -0
- package/.prettierrc +0 -3
- package/README.MD +0 -371
- package/babel.config.json +0 -3
- package/demo/device-activated-promise.js +0 -12
- package/demo/device-activated.js +0 -12
- package/demo/device-deactivated-promise.js +0 -12
- package/demo/device-deactivated.js +0 -12
- package/demo/smartcard-demo.js +0 -100
- package/lib/Card.js +0 -129
- package/lib/CommandApdu.js +0 -109
- package/lib/Device.js +0 -138
- package/lib/Devices.js +0 -134
- package/lib/Iso7816Application.js +0 -169
- package/lib/ResponseApdu.js +0 -129
package/lib/devices.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const EventEmitter = require('events');
|
|
5
|
+
const addon = require('../build/Release/smartcard_napi.node');
|
|
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
|
+
|
|
15
|
+
const { Context, ReaderMonitor } = addon;
|
|
16
|
+
const SCARD_STATE_PRESENT = addon.SCARD_STATE_PRESENT;
|
|
17
|
+
const SCARD_SHARE_SHARED = addon.SCARD_SHARE_SHARED;
|
|
18
|
+
const SCARD_PROTOCOL_T0 = addon.SCARD_PROTOCOL_T0;
|
|
19
|
+
const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} ReaderState
|
|
23
|
+
* @property {boolean} hasCard
|
|
24
|
+
* @property {Card|null} card
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* High-level event-driven API for PC/SC devices
|
|
29
|
+
*
|
|
30
|
+
* Uses native ReaderMonitor for efficient background monitoring
|
|
31
|
+
* with ThreadSafeFunction to emit events from worker thread.
|
|
32
|
+
*
|
|
33
|
+
* Events:
|
|
34
|
+
* - 'reader-attached': Emitted when a reader is attached
|
|
35
|
+
* - 'reader-detached': Emitted when a reader is detached
|
|
36
|
+
* - 'card-inserted': Emitted when a card is inserted
|
|
37
|
+
* - 'card-removed': Emitted when a card is removed
|
|
38
|
+
* - 'error': Emitted on errors
|
|
39
|
+
*
|
|
40
|
+
* @extends EventEmitter
|
|
41
|
+
*/
|
|
42
|
+
class Devices extends EventEmitter {
|
|
43
|
+
constructor() {
|
|
44
|
+
super();
|
|
45
|
+
/** @type {ReaderMonitorType|null} */
|
|
46
|
+
this._monitor = null;
|
|
47
|
+
/** @type {ContextType|null} */
|
|
48
|
+
this._context = null;
|
|
49
|
+
/** @type {boolean} */
|
|
50
|
+
this._running = false;
|
|
51
|
+
/** @type {Map<string, ReaderState>} */
|
|
52
|
+
this._readers = new Map();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Start monitoring for device changes
|
|
57
|
+
*/
|
|
58
|
+
start() {
|
|
59
|
+
if (this._running) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
// Create context for card connections
|
|
65
|
+
this._context = new Context();
|
|
66
|
+
|
|
67
|
+
// Create native monitor
|
|
68
|
+
this._monitor = new ReaderMonitor();
|
|
69
|
+
this._running = true;
|
|
70
|
+
|
|
71
|
+
// Start native monitoring with callback
|
|
72
|
+
this._monitor.start((event) => {
|
|
73
|
+
this._handleEvent(event);
|
|
74
|
+
});
|
|
75
|
+
} catch (err) {
|
|
76
|
+
this.emit('error', err);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Stop monitoring
|
|
82
|
+
*/
|
|
83
|
+
stop() {
|
|
84
|
+
this._running = false;
|
|
85
|
+
|
|
86
|
+
if (this._monitor) {
|
|
87
|
+
try {
|
|
88
|
+
this._monitor.stop();
|
|
89
|
+
} catch (err) {
|
|
90
|
+
// Ignore stop errors
|
|
91
|
+
}
|
|
92
|
+
this._monitor = null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Disconnect any connected cards
|
|
96
|
+
for (const [name, state] of this._readers) {
|
|
97
|
+
if (state.card) {
|
|
98
|
+
try {
|
|
99
|
+
state.card.disconnect();
|
|
100
|
+
} catch (err) {
|
|
101
|
+
// Ignore disconnect errors
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this._readers.clear();
|
|
106
|
+
|
|
107
|
+
if (this._context) {
|
|
108
|
+
try {
|
|
109
|
+
this._context.close();
|
|
110
|
+
} catch (err) {
|
|
111
|
+
// Ignore close errors
|
|
112
|
+
}
|
|
113
|
+
this._context = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* List currently known readers
|
|
119
|
+
* @returns {Reader[]} Array of readers
|
|
120
|
+
*/
|
|
121
|
+
listReaders() {
|
|
122
|
+
if (!this._context || !this._context.isValid) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return this._context.listReaders();
|
|
127
|
+
} catch (err) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Handle events from native monitor
|
|
134
|
+
* @param {MonitorEvent} event
|
|
135
|
+
* @returns {Promise<void>}
|
|
136
|
+
*/
|
|
137
|
+
async _handleEvent(event) {
|
|
138
|
+
if (!this._running) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { type, reader: readerName, state, atr } = event;
|
|
143
|
+
|
|
144
|
+
switch (type) {
|
|
145
|
+
case 'reader-attached':
|
|
146
|
+
this._handleReaderAttached(readerName, state, atr);
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case 'reader-detached':
|
|
150
|
+
this._handleReaderDetached(readerName);
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case 'card-inserted':
|
|
154
|
+
await this._handleCardInserted(readerName, state, atr);
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case 'card-removed':
|
|
158
|
+
this._handleCardRemoved(readerName);
|
|
159
|
+
break;
|
|
160
|
+
|
|
161
|
+
case 'error':
|
|
162
|
+
// readerName contains error message for error events
|
|
163
|
+
this.emit('error', new Error(readerName));
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Handle reader attached
|
|
170
|
+
* @param {string} readerName
|
|
171
|
+
* @param {number} state
|
|
172
|
+
* @param {Buffer|null} atr
|
|
173
|
+
*/
|
|
174
|
+
_handleReaderAttached(readerName, state, atr) {
|
|
175
|
+
// Initialize reader state
|
|
176
|
+
this._readers.set(readerName, {
|
|
177
|
+
hasCard: false,
|
|
178
|
+
card: null,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Create a reader-like object for the event
|
|
182
|
+
const reader = {
|
|
183
|
+
name: readerName,
|
|
184
|
+
state: state,
|
|
185
|
+
atr: atr,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
this.emit('reader-attached', reader);
|
|
189
|
+
|
|
190
|
+
// Check if card is already present
|
|
191
|
+
if ((state & SCARD_STATE_PRESENT) !== 0) {
|
|
192
|
+
this._handleCardInserted(readerName, state, atr);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Handle reader detached
|
|
198
|
+
* @param {string} readerName
|
|
199
|
+
*/
|
|
200
|
+
_handleReaderDetached(readerName) {
|
|
201
|
+
const state = this._readers.get(readerName);
|
|
202
|
+
|
|
203
|
+
// If card was connected, emit card-removed first
|
|
204
|
+
if (state && state.hasCard) {
|
|
205
|
+
this._handleCardRemoved(readerName);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this._readers.delete(readerName);
|
|
209
|
+
|
|
210
|
+
const reader = { name: readerName };
|
|
211
|
+
this.emit('reader-detached', reader);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Handle card inserted
|
|
216
|
+
* @param {string} readerName
|
|
217
|
+
* @param {number} eventState
|
|
218
|
+
* @param {Buffer|null} atr
|
|
219
|
+
* @returns {Promise<void>}
|
|
220
|
+
*/
|
|
221
|
+
async _handleCardInserted(readerName, eventState, atr) {
|
|
222
|
+
let state = this._readers.get(readerName);
|
|
223
|
+
if (!state) {
|
|
224
|
+
state = { hasCard: false, card: null };
|
|
225
|
+
this._readers.set(readerName, state);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
state.hasCard = true;
|
|
229
|
+
|
|
230
|
+
// Try to connect to the card
|
|
231
|
+
try {
|
|
232
|
+
const readers = this._context.listReaders();
|
|
233
|
+
const reader = readers.find(r => r.name === readerName);
|
|
234
|
+
|
|
235
|
+
if (reader) {
|
|
236
|
+
const card = await reader.connect(
|
|
237
|
+
SCARD_SHARE_SHARED,
|
|
238
|
+
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
state.card = card;
|
|
242
|
+
|
|
243
|
+
this.emit('card-inserted', {
|
|
244
|
+
reader: { name: readerName, state: eventState, atr: atr },
|
|
245
|
+
card: card,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
} catch (err) {
|
|
249
|
+
// Emit error but don't fail
|
|
250
|
+
this.emit('error', err);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Handle card removed
|
|
256
|
+
* @param {string} readerName
|
|
257
|
+
*/
|
|
258
|
+
_handleCardRemoved(readerName) {
|
|
259
|
+
const state = this._readers.get(readerName);
|
|
260
|
+
if (!state) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const card = state.card;
|
|
265
|
+
state.hasCard = false;
|
|
266
|
+
state.card = null;
|
|
267
|
+
|
|
268
|
+
if (card) {
|
|
269
|
+
try {
|
|
270
|
+
card.disconnect();
|
|
271
|
+
} catch (err) {
|
|
272
|
+
// Ignore - card is already removed
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
this.emit('card-removed', {
|
|
277
|
+
reader: { name: readerName },
|
|
278
|
+
card: card,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = { Devices };
|
package/lib/errors.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base error class for PC/SC errors
|
|
6
|
+
*/
|
|
7
|
+
class PCSCError extends Error {
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} message
|
|
10
|
+
* @param {number} code
|
|
11
|
+
*/
|
|
12
|
+
constructor(message, code) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'PCSCError';
|
|
15
|
+
this.code = code;
|
|
16
|
+
Error.captureStackTrace(this, this.constructor);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Error thrown when a card is removed during an operation
|
|
22
|
+
*/
|
|
23
|
+
class CardRemovedError extends PCSCError {
|
|
24
|
+
constructor(message = 'Card was removed') {
|
|
25
|
+
super(message, 0x80100069);
|
|
26
|
+
this.name = 'CardRemovedError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Error thrown when an operation times out
|
|
32
|
+
*/
|
|
33
|
+
class TimeoutError extends PCSCError {
|
|
34
|
+
constructor(message = 'Operation timed out') {
|
|
35
|
+
super(message, 0x8010000A);
|
|
36
|
+
this.name = 'TimeoutError';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Error thrown when no readers are available
|
|
42
|
+
*/
|
|
43
|
+
class NoReadersError extends PCSCError {
|
|
44
|
+
constructor(message = 'No readers available') {
|
|
45
|
+
super(message, 0x8010002E);
|
|
46
|
+
this.name = 'NoReadersError';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Error thrown when PC/SC service is not running
|
|
52
|
+
*/
|
|
53
|
+
class ServiceNotRunningError extends PCSCError {
|
|
54
|
+
constructor(message = 'PC/SC service not running') {
|
|
55
|
+
super(message, 0x8010001D);
|
|
56
|
+
this.name = 'ServiceNotRunningError';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Error thrown when there's a sharing violation
|
|
62
|
+
*/
|
|
63
|
+
class SharingViolationError extends PCSCError {
|
|
64
|
+
constructor(message = 'Sharing violation - card is in use') {
|
|
65
|
+
super(message, 0x8010000B);
|
|
66
|
+
this.name = 'SharingViolationError';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
PCSCError,
|
|
72
|
+
CardRemovedError,
|
|
73
|
+
TimeoutError,
|
|
74
|
+
NoReadersError,
|
|
75
|
+
ServiceNotRunningError,
|
|
76
|
+
SharingViolationError,
|
|
77
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reader state information returned from waitForChange
|
|
7
|
+
*/
|
|
8
|
+
export interface ReaderState {
|
|
9
|
+
name: string;
|
|
10
|
+
state: number;
|
|
11
|
+
changed: boolean;
|
|
12
|
+
atr: Buffer | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Card status information
|
|
17
|
+
*/
|
|
18
|
+
export interface CardStatus {
|
|
19
|
+
state: number;
|
|
20
|
+
protocol: number;
|
|
21
|
+
atr: Buffer;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Represents a connected smart card
|
|
26
|
+
*/
|
|
27
|
+
export interface Card {
|
|
28
|
+
/** The active protocol (T0, T1, or RAW) */
|
|
29
|
+
readonly protocol: number;
|
|
30
|
+
/** Whether the card is still connected */
|
|
31
|
+
readonly connected: boolean;
|
|
32
|
+
/** The card's ATR (Answer To Reset) */
|
|
33
|
+
readonly atr: Buffer | null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Transmit an APDU command to the card
|
|
37
|
+
* @param command - The command buffer or byte array
|
|
38
|
+
* @returns Promise resolving to the response buffer
|
|
39
|
+
*/
|
|
40
|
+
transmit(command: Buffer | number[]): Promise<Buffer>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Send a control command to the reader
|
|
44
|
+
* @param code - Control code
|
|
45
|
+
* @param data - Optional data buffer
|
|
46
|
+
* @returns Promise resolving to the response buffer
|
|
47
|
+
*/
|
|
48
|
+
control(code: number, data?: Buffer | number[]): Promise<Buffer>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get the current card status
|
|
52
|
+
* @returns Card status object
|
|
53
|
+
*/
|
|
54
|
+
getStatus(): CardStatus;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Disconnect from the card
|
|
58
|
+
* @param disposition - What to do with the card (default: SCARD_LEAVE_CARD)
|
|
59
|
+
*/
|
|
60
|
+
disconnect(disposition?: number): void;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Reconnect to the card
|
|
64
|
+
* @param shareMode - Share mode
|
|
65
|
+
* @param protocol - Preferred protocol(s)
|
|
66
|
+
* @param initialization - Initialization action
|
|
67
|
+
* @returns The new active protocol
|
|
68
|
+
*/
|
|
69
|
+
reconnect(shareMode?: number, protocol?: number, initialization?: number): number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Represents a smart card reader
|
|
74
|
+
*/
|
|
75
|
+
export interface Reader {
|
|
76
|
+
/** The reader name */
|
|
77
|
+
readonly name: string;
|
|
78
|
+
/** Current reader state flags */
|
|
79
|
+
readonly state: number;
|
|
80
|
+
/** ATR of the card if present */
|
|
81
|
+
readonly atr: Buffer | null;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Connect to a card in the reader
|
|
85
|
+
* @param shareMode - Share mode (default: SCARD_SHARE_SHARED)
|
|
86
|
+
* @param protocol - Preferred protocol(s) (default: T0 | T1)
|
|
87
|
+
* @returns Promise resolving to a Card object
|
|
88
|
+
*/
|
|
89
|
+
connect(shareMode?: number, protocol?: number): Promise<Card>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Low-level PC/SC context
|
|
94
|
+
*/
|
|
95
|
+
export declare class Context {
|
|
96
|
+
constructor();
|
|
97
|
+
|
|
98
|
+
/** Whether the context is still valid */
|
|
99
|
+
readonly isValid: boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* List available readers
|
|
103
|
+
* @returns Array of Reader objects
|
|
104
|
+
*/
|
|
105
|
+
listReaders(): Reader[];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Wait for reader/card state changes
|
|
109
|
+
* @param readers - Optional array of readers to monitor
|
|
110
|
+
* @param timeout - Timeout in milliseconds (default: infinite)
|
|
111
|
+
* @returns Promise resolving to array of reader states
|
|
112
|
+
*/
|
|
113
|
+
waitForChange(readers?: Reader[] | ReaderState[], timeout?: number): Promise<ReaderState[] | null>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Cancel a pending waitForChange call
|
|
117
|
+
*/
|
|
118
|
+
cancel(): void;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Close the context and release resources
|
|
122
|
+
*/
|
|
123
|
+
close(): void;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Monitor event from native ReaderMonitor
|
|
128
|
+
*/
|
|
129
|
+
export interface MonitorEvent {
|
|
130
|
+
type: 'reader-attached' | 'reader-detached' | 'card-inserted' | 'card-removed' | 'error';
|
|
131
|
+
reader: string;
|
|
132
|
+
state: number;
|
|
133
|
+
atr: Buffer | null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Native PC/SC event monitor using ThreadSafeFunction
|
|
138
|
+
* Runs monitoring on a background thread for efficiency
|
|
139
|
+
*/
|
|
140
|
+
export declare class ReaderMonitor {
|
|
141
|
+
constructor();
|
|
142
|
+
|
|
143
|
+
/** Whether the monitor is currently running */
|
|
144
|
+
readonly isRunning: boolean;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Start monitoring for reader/card changes
|
|
148
|
+
* @param callback - Function called when events occur
|
|
149
|
+
*/
|
|
150
|
+
start(callback: (event: MonitorEvent) => void): void;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Stop monitoring
|
|
154
|
+
*/
|
|
155
|
+
stop(): void;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Event types for Devices class
|
|
160
|
+
*/
|
|
161
|
+
export interface DeviceEvents {
|
|
162
|
+
'reader-attached': (reader: Reader) => void;
|
|
163
|
+
'reader-detached': (reader: Reader) => void;
|
|
164
|
+
'card-inserted': (event: { reader: Reader; card: Card }) => void;
|
|
165
|
+
'card-removed': (event: { reader: Reader; card: Card | null }) => void;
|
|
166
|
+
'error': (error: Error) => void;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* High-level event-driven API for monitoring PC/SC devices
|
|
171
|
+
*/
|
|
172
|
+
export declare class Devices extends EventEmitter {
|
|
173
|
+
constructor();
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Start monitoring for device changes
|
|
177
|
+
*/
|
|
178
|
+
start(): void;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Stop monitoring and release resources
|
|
182
|
+
*/
|
|
183
|
+
stop(): void;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* List currently known readers
|
|
187
|
+
* @returns Array of Reader objects
|
|
188
|
+
*/
|
|
189
|
+
listReaders(): Reader[];
|
|
190
|
+
|
|
191
|
+
on<K extends keyof DeviceEvents>(event: K, listener: DeviceEvents[K]): this;
|
|
192
|
+
once<K extends keyof DeviceEvents>(event: K, listener: DeviceEvents[K]): this;
|
|
193
|
+
off<K extends keyof DeviceEvents>(event: K, listener: DeviceEvents[K]): this;
|
|
194
|
+
emit<K extends keyof DeviceEvents>(event: K, ...args: Parameters<DeviceEvents[K]>): boolean;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Base PC/SC error class
|
|
199
|
+
*/
|
|
200
|
+
export declare class PCSCError extends Error {
|
|
201
|
+
readonly code: number;
|
|
202
|
+
constructor(message: string, code: number);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Error thrown when card is removed during operation
|
|
207
|
+
*/
|
|
208
|
+
export declare class CardRemovedError extends PCSCError {
|
|
209
|
+
constructor(message?: string);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Error thrown when operation times out
|
|
214
|
+
*/
|
|
215
|
+
export declare class TimeoutError extends PCSCError {
|
|
216
|
+
constructor(message?: string);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Share modes
|
|
220
|
+
export declare const SCARD_SHARE_EXCLUSIVE: number;
|
|
221
|
+
export declare const SCARD_SHARE_SHARED: number;
|
|
222
|
+
export declare const SCARD_SHARE_DIRECT: number;
|
|
223
|
+
|
|
224
|
+
// Protocols
|
|
225
|
+
export declare const SCARD_PROTOCOL_T0: number;
|
|
226
|
+
export declare const SCARD_PROTOCOL_T1: number;
|
|
227
|
+
export declare const SCARD_PROTOCOL_RAW: number;
|
|
228
|
+
export declare const SCARD_PROTOCOL_UNDEFINED: number;
|
|
229
|
+
|
|
230
|
+
// Disposition
|
|
231
|
+
export declare const SCARD_LEAVE_CARD: number;
|
|
232
|
+
export declare const SCARD_RESET_CARD: number;
|
|
233
|
+
export declare const SCARD_UNPOWER_CARD: number;
|
|
234
|
+
export declare const SCARD_EJECT_CARD: number;
|
|
235
|
+
|
|
236
|
+
// State flags
|
|
237
|
+
export declare const SCARD_STATE_UNAWARE: number;
|
|
238
|
+
export declare const SCARD_STATE_IGNORE: number;
|
|
239
|
+
export declare const SCARD_STATE_CHANGED: number;
|
|
240
|
+
export declare const SCARD_STATE_UNKNOWN: number;
|
|
241
|
+
export declare const SCARD_STATE_UNAVAILABLE: number;
|
|
242
|
+
export declare const SCARD_STATE_EMPTY: number;
|
|
243
|
+
export declare const SCARD_STATE_PRESENT: number;
|
|
244
|
+
export declare const SCARD_STATE_ATRMATCH: number;
|
|
245
|
+
export declare const SCARD_STATE_EXCLUSIVE: number;
|
|
246
|
+
export declare const SCARD_STATE_INUSE: number;
|
|
247
|
+
export declare const SCARD_STATE_MUTE: number;
|
package/lib/index.js
CHANGED
|
@@ -1,24 +1,85 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
// Load native addon
|
|
5
|
+
const addon = require('../build/Release/smartcard_napi.node');
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
// Re-export native classes
|
|
8
|
+
const { Context, Reader, Card, ReaderMonitor } = addon;
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
// Re-export constants
|
|
11
|
+
const SCARD_SHARE_EXCLUSIVE = addon.SCARD_SHARE_EXCLUSIVE;
|
|
12
|
+
const SCARD_SHARE_SHARED = addon.SCARD_SHARE_SHARED;
|
|
13
|
+
const SCARD_SHARE_DIRECT = addon.SCARD_SHARE_DIRECT;
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
const SCARD_PROTOCOL_T0 = addon.SCARD_PROTOCOL_T0;
|
|
16
|
+
const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
|
|
17
|
+
const SCARD_PROTOCOL_RAW = addon.SCARD_PROTOCOL_RAW;
|
|
18
|
+
const SCARD_PROTOCOL_UNDEFINED = addon.SCARD_PROTOCOL_UNDEFINED;
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
const SCARD_LEAVE_CARD = addon.SCARD_LEAVE_CARD;
|
|
21
|
+
const SCARD_RESET_CARD = addon.SCARD_RESET_CARD;
|
|
22
|
+
const SCARD_UNPOWER_CARD = addon.SCARD_UNPOWER_CARD;
|
|
23
|
+
const SCARD_EJECT_CARD = addon.SCARD_EJECT_CARD;
|
|
12
24
|
|
|
13
|
-
|
|
25
|
+
const SCARD_STATE_UNAWARE = addon.SCARD_STATE_UNAWARE;
|
|
26
|
+
const SCARD_STATE_IGNORE = addon.SCARD_STATE_IGNORE;
|
|
27
|
+
const SCARD_STATE_CHANGED = addon.SCARD_STATE_CHANGED;
|
|
28
|
+
const SCARD_STATE_UNKNOWN = addon.SCARD_STATE_UNKNOWN;
|
|
29
|
+
const SCARD_STATE_UNAVAILABLE = addon.SCARD_STATE_UNAVAILABLE;
|
|
30
|
+
const SCARD_STATE_EMPTY = addon.SCARD_STATE_EMPTY;
|
|
31
|
+
const SCARD_STATE_PRESENT = addon.SCARD_STATE_PRESENT;
|
|
32
|
+
const SCARD_STATE_ATRMATCH = addon.SCARD_STATE_ATRMATCH;
|
|
33
|
+
const SCARD_STATE_EXCLUSIVE = addon.SCARD_STATE_EXCLUSIVE;
|
|
34
|
+
const SCARD_STATE_INUSE = addon.SCARD_STATE_INUSE;
|
|
35
|
+
const SCARD_STATE_MUTE = addon.SCARD_STATE_MUTE;
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
// Import Devices class
|
|
38
|
+
const { Devices } = require('./devices');
|
|
39
|
+
|
|
40
|
+
// Import error classes
|
|
41
|
+
const { PCSCError, CardRemovedError, TimeoutError } = require('./errors');
|
|
16
42
|
|
|
17
43
|
module.exports = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
44
|
+
// Classes
|
|
45
|
+
Context,
|
|
46
|
+
Reader,
|
|
47
|
+
Card,
|
|
48
|
+
Devices,
|
|
49
|
+
ReaderMonitor,
|
|
50
|
+
|
|
51
|
+
// Error classes
|
|
52
|
+
PCSCError,
|
|
53
|
+
CardRemovedError,
|
|
54
|
+
TimeoutError,
|
|
55
|
+
|
|
56
|
+
// Share modes
|
|
57
|
+
SCARD_SHARE_EXCLUSIVE,
|
|
58
|
+
SCARD_SHARE_SHARED,
|
|
59
|
+
SCARD_SHARE_DIRECT,
|
|
60
|
+
|
|
61
|
+
// Protocols
|
|
62
|
+
SCARD_PROTOCOL_T0,
|
|
63
|
+
SCARD_PROTOCOL_T1,
|
|
64
|
+
SCARD_PROTOCOL_RAW,
|
|
65
|
+
SCARD_PROTOCOL_UNDEFINED,
|
|
66
|
+
|
|
67
|
+
// Disposition
|
|
68
|
+
SCARD_LEAVE_CARD,
|
|
69
|
+
SCARD_RESET_CARD,
|
|
70
|
+
SCARD_UNPOWER_CARD,
|
|
71
|
+
SCARD_EJECT_CARD,
|
|
72
|
+
|
|
73
|
+
// State flags
|
|
74
|
+
SCARD_STATE_UNAWARE,
|
|
75
|
+
SCARD_STATE_IGNORE,
|
|
76
|
+
SCARD_STATE_CHANGED,
|
|
77
|
+
SCARD_STATE_UNKNOWN,
|
|
78
|
+
SCARD_STATE_UNAVAILABLE,
|
|
79
|
+
SCARD_STATE_EMPTY,
|
|
80
|
+
SCARD_STATE_PRESENT,
|
|
81
|
+
SCARD_STATE_ATRMATCH,
|
|
82
|
+
SCARD_STATE_EXCLUSIVE,
|
|
83
|
+
SCARD_STATE_INUSE,
|
|
84
|
+
SCARD_STATE_MUTE,
|
|
85
|
+
};
|