smartcard 2.0.2 → 3.1.0

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
@@ -289,11 +289,11 @@ interface Card {
289
289
  readonly protocol: number;
290
290
  readonly connected: boolean;
291
291
  readonly atr: Buffer | null;
292
- transmit(command: Buffer | number[]): Promise<Buffer>;
292
+ transmit(command: Buffer | number[], options?: { maxRecvLength?: number }): Promise<Buffer>;
293
293
  control(code: number, data?: Buffer): Promise<Buffer>;
294
294
  getStatus(): { state: number; protocol: number; atr: Buffer };
295
295
  disconnect(disposition?: number): void;
296
- reconnect(shareMode?: number, protocol?: number, init?: number): number;
296
+ reconnect(shareMode?: number, protocol?: number, init?: number): Promise<number>;
297
297
  }
298
298
  ```
299
299
 
@@ -0,0 +1,107 @@
1
+ // @ts-check
2
+ 'use strict';
3
+
4
+ /**
5
+ * Control codes and feature constants for PC/SC smart card operations
6
+ */
7
+
8
+ /**
9
+ * Generate a control code (platform-specific)
10
+ * @param {number} code - The control code number
11
+ * @returns {number} The platform-specific control code
12
+ */
13
+ function SCARD_CTL_CODE(code) {
14
+ if (process.platform === 'win32') {
15
+ // Windows: (FILE_DEVICE_SMARTCARD << 16) + (code << 2)
16
+ // FILE_DEVICE_SMARTCARD = 0x31
17
+ return (0x31 << 16) + (code << 2);
18
+ } else {
19
+ // macOS/Linux: 0x42000000 + code
20
+ return 0x42000000 + code;
21
+ }
22
+ }
23
+
24
+ // Common control codes
25
+ const CM_IOCTL_GET_FEATURE_REQUEST = SCARD_CTL_CODE(3400);
26
+
27
+ // CCID Feature tags (from CCID spec)
28
+ const FEATURE_VERIFY_PIN_START = 0x01;
29
+ const FEATURE_VERIFY_PIN_FINISH = 0x02;
30
+ const FEATURE_MODIFY_PIN_START = 0x03;
31
+ const FEATURE_MODIFY_PIN_FINISH = 0x04;
32
+ const FEATURE_GET_KEY_PRESSED = 0x05;
33
+ const FEATURE_VERIFY_PIN_DIRECT = 0x06;
34
+ const FEATURE_MODIFY_PIN_DIRECT = 0x07;
35
+ const FEATURE_MCT_READER_DIRECT = 0x08;
36
+ const FEATURE_MCT_UNIVERSAL = 0x09;
37
+ const FEATURE_IFD_PIN_PROPERTIES = 0x0A;
38
+ const FEATURE_ABORT = 0x0B;
39
+ const FEATURE_SET_SPE_MESSAGE = 0x0C;
40
+ const FEATURE_VERIFY_PIN_DIRECT_APP_ID = 0x0D;
41
+ const FEATURE_MODIFY_PIN_DIRECT_APP_ID = 0x0E;
42
+ const FEATURE_WRITE_DISPLAY = 0x0F;
43
+ const FEATURE_GET_KEY = 0x10;
44
+ const FEATURE_IFD_DISPLAY_PROPERTIES = 0x11;
45
+ const FEATURE_GET_TLV_PROPERTIES = 0x12;
46
+ const FEATURE_CCID_ESC_COMMAND = 0x13;
47
+
48
+ /**
49
+ * Parse feature TLV response from CM_IOCTL_GET_FEATURE_REQUEST
50
+ * @param {Buffer} response - The TLV response buffer
51
+ * @returns {Map<number, number>} Map of feature tag to control code
52
+ */
53
+ function parseFeatures(response) {
54
+ const features = new Map();
55
+ let offset = 0;
56
+
57
+ while (offset + 4 <= response.length) {
58
+ const tag = response[offset];
59
+ const length = response[offset + 1];
60
+
61
+ if (length === 4 && offset + 2 + length <= response.length) {
62
+ // Big-endian control code
63
+ const controlCode =
64
+ (response[offset + 2] << 24) |
65
+ (response[offset + 3] << 16) |
66
+ (response[offset + 4] << 8) |
67
+ response[offset + 5];
68
+ features.set(tag, controlCode);
69
+ }
70
+
71
+ offset += 2 + length;
72
+ }
73
+
74
+ return features;
75
+ }
76
+
77
+ module.exports = {
78
+ // Control code generator
79
+ SCARD_CTL_CODE,
80
+
81
+ // Common control codes
82
+ CM_IOCTL_GET_FEATURE_REQUEST,
83
+
84
+ // CCID Feature tags
85
+ FEATURE_VERIFY_PIN_START,
86
+ FEATURE_VERIFY_PIN_FINISH,
87
+ FEATURE_MODIFY_PIN_START,
88
+ FEATURE_MODIFY_PIN_FINISH,
89
+ FEATURE_GET_KEY_PRESSED,
90
+ FEATURE_VERIFY_PIN_DIRECT,
91
+ FEATURE_MODIFY_PIN_DIRECT,
92
+ FEATURE_MCT_READER_DIRECT,
93
+ FEATURE_MCT_UNIVERSAL,
94
+ FEATURE_IFD_PIN_PROPERTIES,
95
+ FEATURE_ABORT,
96
+ FEATURE_SET_SPE_MESSAGE,
97
+ FEATURE_VERIFY_PIN_DIRECT_APP_ID,
98
+ FEATURE_MODIFY_PIN_DIRECT_APP_ID,
99
+ FEATURE_WRITE_DISPLAY,
100
+ FEATURE_GET_KEY,
101
+ FEATURE_IFD_DISPLAY_PROPERTIES,
102
+ FEATURE_GET_TLV_PROPERTIES,
103
+ FEATURE_CCID_ESC_COMMAND,
104
+
105
+ // Helper functions
106
+ parseFeatures,
107
+ };
package/lib/devices.js CHANGED
@@ -246,10 +246,27 @@ class Devices extends EventEmitter {
246
246
  const reader = readers.find(r => r.name === readerName);
247
247
 
248
248
  if (reader) {
249
- const card = await reader.connect(
250
- SCARD_SHARE_SHARED,
251
- SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1
252
- );
249
+ let card;
250
+ try {
251
+ // First try with both T=0 and T=1 protocols
252
+ card = await reader.connect(
253
+ SCARD_SHARE_SHARED,
254
+ SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1
255
+ );
256
+ } catch (dualProtocolErr) {
257
+ // If dual protocol fails (e.g., SCARD_W_UNRESPONSIVE_CARD),
258
+ // fallback to T=0 only (issue #34)
259
+ if (dualProtocolErr.message &&
260
+ dualProtocolErr.message.toLowerCase().includes('unresponsive')) {
261
+ card = await reader.connect(
262
+ SCARD_SHARE_SHARED,
263
+ SCARD_PROTOCOL_T0
264
+ );
265
+ } else {
266
+ // Re-throw if it's a different error
267
+ throw dualProtocolErr;
268
+ }
269
+ }
253
270
 
254
271
  state.card = card;
255
272
 
package/lib/errors.js CHANGED
@@ -67,6 +67,32 @@ class SharingViolationError extends PCSCError {
67
67
  }
68
68
  }
69
69
 
70
+ /**
71
+ * PC/SC error codes mapped to specific error classes
72
+ * @type {Map<number, typeof PCSCError>}
73
+ */
74
+ const ERROR_CODE_MAP = new Map([
75
+ [0x80100069, CardRemovedError], // SCARD_W_REMOVED_CARD
76
+ [0x8010000A, TimeoutError], // SCARD_E_TIMEOUT
77
+ [0x8010002E, NoReadersError], // SCARD_E_NO_READERS_AVAILABLE
78
+ [0x8010001D, ServiceNotRunningError], // SCARD_E_NO_SERVICE
79
+ [0x8010000B, SharingViolationError], // SCARD_E_SHARING_VIOLATION
80
+ ]);
81
+
82
+ /**
83
+ * Factory function to create the appropriate error class based on PC/SC error code
84
+ * @param {string} message - Error message
85
+ * @param {number} code - PC/SC error code
86
+ * @returns {PCSCError} The appropriate error instance
87
+ */
88
+ function createPCSCError(message, code) {
89
+ const ErrorClass = ERROR_CODE_MAP.get(code);
90
+ if (ErrorClass) {
91
+ return new ErrorClass(message);
92
+ }
93
+ return new PCSCError(message, code);
94
+ }
95
+
70
96
  module.exports = {
71
97
  PCSCError,
72
98
  CardRemovedError,
@@ -74,4 +100,5 @@ module.exports = {
74
100
  NoReadersError,
75
101
  ServiceNotRunningError,
76
102
  SharingViolationError,
103
+ createPCSCError,
77
104
  };
package/lib/index.d.ts CHANGED
@@ -21,6 +21,18 @@ export interface CardStatus {
21
21
  atr: Buffer;
22
22
  }
23
23
 
24
+ /**
25
+ * Options for card.transmit()
26
+ */
27
+ export interface TransmitOptions {
28
+ /**
29
+ * Maximum receive buffer size in bytes.
30
+ * Default: 258 (standard APDU: 256 data + 2 status bytes)
31
+ * Maximum: 262144 (256KB for extended APDUs)
32
+ */
33
+ maxRecvLength?: number;
34
+ }
35
+
24
36
  /**
25
37
  * Represents a connected smart card
26
38
  */
@@ -35,9 +47,10 @@ export interface Card {
35
47
  /**
36
48
  * Transmit an APDU command to the card
37
49
  * @param command - The command buffer or byte array
50
+ * @param options - Optional transmit options
38
51
  * @returns Promise resolving to the response buffer
39
52
  */
40
- transmit(command: Buffer | number[]): Promise<Buffer>;
53
+ transmit(command: Buffer | number[], options?: TransmitOptions): Promise<Buffer>;
41
54
 
42
55
  /**
43
56
  * Send a control command to the reader
@@ -60,13 +73,13 @@ export interface Card {
60
73
  disconnect(disposition?: number): void;
61
74
 
62
75
  /**
63
- * Reconnect to the card
76
+ * Reconnect to the card (async)
64
77
  * @param shareMode - Share mode
65
78
  * @param protocol - Preferred protocol(s)
66
79
  * @param initialization - Initialization action
67
- * @returns The new active protocol
80
+ * @returns Promise resolving to the new active protocol
68
81
  */
69
- reconnect(shareMode?: number, protocol?: number, initialization?: number): number;
82
+ reconnect(shareMode?: number, protocol?: number, initialization?: number): Promise<number>;
70
83
  }
71
84
 
72
85
  /**
@@ -216,6 +229,32 @@ export declare class TimeoutError extends PCSCError {
216
229
  constructor(message?: string);
217
230
  }
218
231
 
232
+ /**
233
+ * Error thrown when no readers are available
234
+ */
235
+ export declare class NoReadersError extends PCSCError {
236
+ constructor(message?: string);
237
+ }
238
+
239
+ /**
240
+ * Error thrown when PC/SC service is not running
241
+ */
242
+ export declare class ServiceNotRunningError extends PCSCError {
243
+ constructor(message?: string);
244
+ }
245
+
246
+ /**
247
+ * Error thrown when there's a sharing violation
248
+ */
249
+ export declare class SharingViolationError extends PCSCError {
250
+ constructor(message?: string);
251
+ }
252
+
253
+ /**
254
+ * Factory function to create the appropriate error class based on PC/SC error code
255
+ */
256
+ export declare function createPCSCError(message: string, code: number): PCSCError;
257
+
219
258
  // Share modes
220
259
  export declare const SCARD_SHARE_EXCLUSIVE: number;
221
260
  export declare const SCARD_SHARE_SHARED: number;
@@ -245,3 +284,42 @@ export declare const SCARD_STATE_ATRMATCH: number;
245
284
  export declare const SCARD_STATE_EXCLUSIVE: number;
246
285
  export declare const SCARD_STATE_INUSE: number;
247
286
  export declare const SCARD_STATE_MUTE: number;
287
+
288
+ // Control code generator
289
+ /**
290
+ * Generate a platform-specific control code
291
+ * @param code - The control code number
292
+ * @returns The platform-specific control code
293
+ */
294
+ export declare function SCARD_CTL_CODE(code: number): number;
295
+
296
+ // Common control codes
297
+ export declare const CM_IOCTL_GET_FEATURE_REQUEST: number;
298
+
299
+ // CCID Feature tags
300
+ export declare const FEATURE_VERIFY_PIN_START: number;
301
+ export declare const FEATURE_VERIFY_PIN_FINISH: number;
302
+ export declare const FEATURE_MODIFY_PIN_START: number;
303
+ export declare const FEATURE_MODIFY_PIN_FINISH: number;
304
+ export declare const FEATURE_GET_KEY_PRESSED: number;
305
+ export declare const FEATURE_VERIFY_PIN_DIRECT: number;
306
+ export declare const FEATURE_MODIFY_PIN_DIRECT: number;
307
+ export declare const FEATURE_MCT_READER_DIRECT: number;
308
+ export declare const FEATURE_MCT_UNIVERSAL: number;
309
+ export declare const FEATURE_IFD_PIN_PROPERTIES: number;
310
+ export declare const FEATURE_ABORT: number;
311
+ export declare const FEATURE_SET_SPE_MESSAGE: number;
312
+ export declare const FEATURE_VERIFY_PIN_DIRECT_APP_ID: number;
313
+ export declare const FEATURE_MODIFY_PIN_DIRECT_APP_ID: number;
314
+ export declare const FEATURE_WRITE_DISPLAY: number;
315
+ export declare const FEATURE_GET_KEY: number;
316
+ export declare const FEATURE_IFD_DISPLAY_PROPERTIES: number;
317
+ export declare const FEATURE_GET_TLV_PROPERTIES: number;
318
+ export declare const FEATURE_CCID_ESC_COMMAND: number;
319
+
320
+ /**
321
+ * Parse feature TLV response from CM_IOCTL_GET_FEATURE_REQUEST
322
+ * @param response - The TLV response buffer
323
+ * @returns Map of feature tag to control code
324
+ */
325
+ export declare function parseFeatures(response: Buffer): Map<number, number>;
package/lib/index.js CHANGED
@@ -38,7 +38,41 @@ const SCARD_STATE_MUTE = addon.SCARD_STATE_MUTE;
38
38
  const { Devices } = require('./devices');
39
39
 
40
40
  // Import error classes
41
- const { PCSCError, CardRemovedError, TimeoutError } = require('./errors');
41
+ const {
42
+ PCSCError,
43
+ CardRemovedError,
44
+ TimeoutError,
45
+ NoReadersError,
46
+ ServiceNotRunningError,
47
+ SharingViolationError,
48
+ createPCSCError,
49
+ } = require('./errors');
50
+
51
+ // Import control codes
52
+ const {
53
+ SCARD_CTL_CODE,
54
+ CM_IOCTL_GET_FEATURE_REQUEST,
55
+ FEATURE_VERIFY_PIN_START,
56
+ FEATURE_VERIFY_PIN_FINISH,
57
+ FEATURE_MODIFY_PIN_START,
58
+ FEATURE_MODIFY_PIN_FINISH,
59
+ FEATURE_GET_KEY_PRESSED,
60
+ FEATURE_VERIFY_PIN_DIRECT,
61
+ FEATURE_MODIFY_PIN_DIRECT,
62
+ FEATURE_MCT_READER_DIRECT,
63
+ FEATURE_MCT_UNIVERSAL,
64
+ FEATURE_IFD_PIN_PROPERTIES,
65
+ FEATURE_ABORT,
66
+ FEATURE_SET_SPE_MESSAGE,
67
+ FEATURE_VERIFY_PIN_DIRECT_APP_ID,
68
+ FEATURE_MODIFY_PIN_DIRECT_APP_ID,
69
+ FEATURE_WRITE_DISPLAY,
70
+ FEATURE_GET_KEY,
71
+ FEATURE_IFD_DISPLAY_PROPERTIES,
72
+ FEATURE_GET_TLV_PROPERTIES,
73
+ FEATURE_CCID_ESC_COMMAND,
74
+ parseFeatures,
75
+ } = require('./control-codes');
42
76
 
43
77
  module.exports = {
44
78
  // Classes
@@ -52,6 +86,10 @@ module.exports = {
52
86
  PCSCError,
53
87
  CardRemovedError,
54
88
  TimeoutError,
89
+ NoReadersError,
90
+ ServiceNotRunningError,
91
+ SharingViolationError,
92
+ createPCSCError,
55
93
 
56
94
  // Share modes
57
95
  SCARD_SHARE_EXCLUSIVE,
@@ -82,4 +120,32 @@ module.exports = {
82
120
  SCARD_STATE_EXCLUSIVE,
83
121
  SCARD_STATE_INUSE,
84
122
  SCARD_STATE_MUTE,
123
+
124
+ // Control codes
125
+ SCARD_CTL_CODE,
126
+ CM_IOCTL_GET_FEATURE_REQUEST,
127
+
128
+ // CCID Feature tags
129
+ FEATURE_VERIFY_PIN_START,
130
+ FEATURE_VERIFY_PIN_FINISH,
131
+ FEATURE_MODIFY_PIN_START,
132
+ FEATURE_MODIFY_PIN_FINISH,
133
+ FEATURE_GET_KEY_PRESSED,
134
+ FEATURE_VERIFY_PIN_DIRECT,
135
+ FEATURE_MODIFY_PIN_DIRECT,
136
+ FEATURE_MCT_READER_DIRECT,
137
+ FEATURE_MCT_UNIVERSAL,
138
+ FEATURE_IFD_PIN_PROPERTIES,
139
+ FEATURE_ABORT,
140
+ FEATURE_SET_SPE_MESSAGE,
141
+ FEATURE_VERIFY_PIN_DIRECT_APP_ID,
142
+ FEATURE_MODIFY_PIN_DIRECT_APP_ID,
143
+ FEATURE_WRITE_DISPLAY,
144
+ FEATURE_GET_KEY,
145
+ FEATURE_IFD_DISPLAY_PROPERTIES,
146
+ FEATURE_GET_TLV_PROPERTIES,
147
+ FEATURE_CCID_ESC_COMMAND,
148
+
149
+ // Helper functions
150
+ parseFeatures,
85
151
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartcard",
3
- "version": "2.0.2",
3
+ "version": "3.1.0",
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",
@@ -19,7 +19,9 @@
19
19
  "install": "node-gyp rebuild",
20
20
  "build": "node-gyp rebuild",
21
21
  "rebuild": "node-gyp rebuild",
22
- "test": "node test/test.js"
22
+ "test": "node test/test.js && node test/unit.js",
23
+ "test:unit": "node test/unit.js",
24
+ "test:integration": "node test/test.js"
23
25
  },
24
26
  "files": [
25
27
  "lib/",
@@ -84,6 +84,7 @@ TransmitWorker::TransmitWorker(
84
84
  SCARDHANDLE card,
85
85
  DWORD protocol,
86
86
  std::vector<uint8_t> sendBuffer,
87
+ size_t maxRecvLength,
87
88
  Napi::Promise::Deferred deferred)
88
89
  : Napi::AsyncWorker(env),
89
90
  card_(card),
@@ -92,8 +93,16 @@ TransmitWorker::TransmitWorker(
92
93
  recvLength_(0),
93
94
  result_(SCARD_S_SUCCESS),
94
95
  deferred_(deferred) {
95
- // Pre-allocate receive buffer (max APDU response size)
96
- recvBuffer_.resize(258);
96
+ // Pre-allocate receive buffer with configurable size
97
+ // Default is 258 (standard APDU: 256 data + 2 status bytes)
98
+ // Max is 262144 (256KB) for extended APDUs
99
+ size_t bufferSize = maxRecvLength;
100
+ if (bufferSize == 0) {
101
+ bufferSize = 258; // Default
102
+ } else if (bufferSize > 262144) {
103
+ bufferSize = 262144; // Cap at 256KB
104
+ }
105
+ recvBuffer_.resize(bufferSize);
97
106
  }
98
107
 
99
108
  void TransmitWorker::Execute() {
@@ -232,3 +241,48 @@ void ConnectWorker::OnOK() {
232
241
  void ConnectWorker::OnError(const Napi::Error& error) {
233
242
  deferred_.Reject(error.Value());
234
243
  }
244
+
245
+ // ============================================================================
246
+ // ReconnectWorker
247
+ // ============================================================================
248
+
249
+ ReconnectWorker::ReconnectWorker(
250
+ Napi::Env env,
251
+ SCARDHANDLE card,
252
+ DWORD shareMode,
253
+ DWORD preferredProtocols,
254
+ DWORD initialization,
255
+ Napi::Promise::Deferred deferred)
256
+ : Napi::AsyncWorker(env),
257
+ card_(card),
258
+ shareMode_(shareMode),
259
+ preferredProtocols_(preferredProtocols),
260
+ initialization_(initialization),
261
+ activeProtocol_(0),
262
+ result_(SCARD_S_SUCCESS),
263
+ deferred_(deferred) {
264
+ }
265
+
266
+ void ReconnectWorker::Execute() {
267
+ result_ = SCardReconnect(
268
+ card_,
269
+ shareMode_,
270
+ preferredProtocols_,
271
+ initialization_,
272
+ &activeProtocol_
273
+ );
274
+ }
275
+
276
+ void ReconnectWorker::OnOK() {
277
+ Napi::Env env = Env();
278
+
279
+ if (result_ == SCARD_S_SUCCESS) {
280
+ deferred_.Resolve(Napi::Number::New(env, activeProtocol_));
281
+ } else {
282
+ deferred_.Reject(Napi::Error::New(env, GetPCSCErrorString(result_)).Value());
283
+ }
284
+ }
285
+
286
+ void ReconnectWorker::OnError(const Napi::Error& error) {
287
+ deferred_.Reject(error.Value());
288
+ }
@@ -35,6 +35,7 @@ public:
35
35
  SCARDHANDLE card,
36
36
  DWORD protocol,
37
37
  std::vector<uint8_t> sendBuffer,
38
+ size_t maxRecvLength,
38
39
  Napi::Promise::Deferred deferred);
39
40
 
40
41
  void Execute() override;
@@ -98,3 +99,29 @@ private:
98
99
  LONG result_;
99
100
  Napi::Promise::Deferred deferred_;
100
101
  };
102
+
103
+ // Async worker for SCardReconnect
104
+ class ReconnectWorker : public Napi::AsyncWorker {
105
+ public:
106
+ ReconnectWorker(Napi::Env env,
107
+ SCARDHANDLE card,
108
+ DWORD shareMode,
109
+ DWORD preferredProtocols,
110
+ DWORD initialization,
111
+ Napi::Promise::Deferred deferred);
112
+
113
+ void Execute() override;
114
+ void OnOK() override;
115
+ void OnError(const Napi::Error& error) override;
116
+
117
+ DWORD GetActiveProtocol() const { return activeProtocol_; }
118
+
119
+ private:
120
+ SCARDHANDLE card_;
121
+ DWORD shareMode_;
122
+ DWORD preferredProtocols_;
123
+ DWORD initialization_;
124
+ DWORD activeProtocol_;
125
+ LONG result_;
126
+ Napi::Promise::Deferred deferred_;
127
+ };
package/src/pcsc_card.cpp CHANGED
@@ -111,11 +111,20 @@ Napi::Value PCSCCard::Transmit(const Napi::CallbackInfo& info) {
111
111
  return env.Null();
112
112
  }
113
113
 
114
+ // Parse options (optional second argument)
115
+ size_t maxRecvLength = 0; // 0 means use default
116
+ if (info.Length() > 1 && info[1].IsObject()) {
117
+ Napi::Object options = info[1].As<Napi::Object>();
118
+ if (options.Has("maxRecvLength") && options.Get("maxRecvLength").IsNumber()) {
119
+ maxRecvLength = options.Get("maxRecvLength").As<Napi::Number>().Uint32Value();
120
+ }
121
+ }
122
+
114
123
  // Create promise for async transmit
115
124
  Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
116
125
 
117
126
  TransmitWorker* worker = new TransmitWorker(
118
- env, card_, protocol_, sendBuffer, deferred);
127
+ env, card_, protocol_, sendBuffer, maxRecvLength, deferred);
119
128
  worker->Queue();
120
129
 
121
130
  return deferred.Promise();
@@ -235,14 +244,12 @@ Napi::Value PCSCCard::Reconnect(const Napi::CallbackInfo& info) {
235
244
  initialization = info[2].As<Napi::Number>().Uint32Value();
236
245
  }
237
246
 
238
- DWORD activeProtocol = 0;
239
- LONG result = SCardReconnect(card_, shareMode, preferredProtocols, initialization, &activeProtocol);
247
+ // Create promise for async reconnect
248
+ Napi::Promise::Deferred deferred = Napi::Promise::Deferred::New(env);
240
249
 
241
- if (result != SCARD_S_SUCCESS) {
242
- Napi::Error::New(env, GetPCSCErrorString(result)).ThrowAsJavaScriptException();
243
- return env.Null();
244
- }
250
+ ReconnectWorker* worker = new ReconnectWorker(
251
+ env, card_, shareMode, preferredProtocols, initialization, deferred);
252
+ worker->Queue();
245
253
 
246
- protocol_ = activeProtocol;
247
- return Napi::Number::New(env, activeProtocol);
254
+ return deferred.Promise();
248
255
  }