smartcard 1.0.46 → 2.0.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/lib/errors.js ADDED
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Base error class for PC/SC errors
5
+ */
6
+ class PCSCError extends Error {
7
+ constructor(message, code) {
8
+ super(message);
9
+ this.name = 'PCSCError';
10
+ this.code = code;
11
+ Error.captureStackTrace(this, this.constructor);
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Error thrown when a card is removed during an operation
17
+ */
18
+ class CardRemovedError extends PCSCError {
19
+ constructor(message = 'Card was removed') {
20
+ super(message, 0x80100069);
21
+ this.name = 'CardRemovedError';
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Error thrown when an operation times out
27
+ */
28
+ class TimeoutError extends PCSCError {
29
+ constructor(message = 'Operation timed out') {
30
+ super(message, 0x8010000A);
31
+ this.name = 'TimeoutError';
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Error thrown when no readers are available
37
+ */
38
+ class NoReadersError extends PCSCError {
39
+ constructor(message = 'No readers available') {
40
+ super(message, 0x8010002E);
41
+ this.name = 'NoReadersError';
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Error thrown when PC/SC service is not running
47
+ */
48
+ class ServiceNotRunningError extends PCSCError {
49
+ constructor(message = 'PC/SC service not running') {
50
+ super(message, 0x8010001D);
51
+ this.name = 'ServiceNotRunningError';
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Error thrown when there's a sharing violation
57
+ */
58
+ class SharingViolationError extends PCSCError {
59
+ constructor(message = 'Sharing violation - card is in use') {
60
+ super(message, 0x8010000B);
61
+ this.name = 'SharingViolationError';
62
+ }
63
+ }
64
+
65
+ module.exports = {
66
+ PCSCError,
67
+ CardRemovedError,
68
+ TimeoutError,
69
+ NoReadersError,
70
+ ServiceNotRunningError,
71
+ SharingViolationError,
72
+ };
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,86 @@
1
1
  'use strict';
2
2
 
3
- var _Iso7816Application = _interopRequireDefault(require("./Iso7816Application"));
3
+ const path = require('path');
4
4
 
5
- var _CommandApdu = _interopRequireDefault(require("./CommandApdu"));
5
+ // Load native addon
6
+ const addon = require('../build/Release/smartcard_napi.node');
6
7
 
7
- var _ResponseApdu = _interopRequireDefault(require("./ResponseApdu"));
8
+ // Re-export native classes
9
+ const { Context, Reader, Card, ReaderMonitor } = addon;
8
10
 
9
- var _Devices = _interopRequireDefault(require("./Devices"));
11
+ // Re-export constants
12
+ const SCARD_SHARE_EXCLUSIVE = addon.SCARD_SHARE_EXCLUSIVE;
13
+ const SCARD_SHARE_SHARED = addon.SCARD_SHARE_SHARED;
14
+ const SCARD_SHARE_DIRECT = addon.SCARD_SHARE_DIRECT;
10
15
 
11
- var _Device = _interopRequireDefault(require("./Device"));
16
+ const SCARD_PROTOCOL_T0 = addon.SCARD_PROTOCOL_T0;
17
+ const SCARD_PROTOCOL_T1 = addon.SCARD_PROTOCOL_T1;
18
+ const SCARD_PROTOCOL_RAW = addon.SCARD_PROTOCOL_RAW;
19
+ const SCARD_PROTOCOL_UNDEFINED = addon.SCARD_PROTOCOL_UNDEFINED;
12
20
 
13
- var _Card = _interopRequireDefault(require("./Card"));
21
+ const SCARD_LEAVE_CARD = addon.SCARD_LEAVE_CARD;
22
+ const SCARD_RESET_CARD = addon.SCARD_RESET_CARD;
23
+ const SCARD_UNPOWER_CARD = addon.SCARD_UNPOWER_CARD;
24
+ const SCARD_EJECT_CARD = addon.SCARD_EJECT_CARD;
14
25
 
15
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
26
+ const SCARD_STATE_UNAWARE = addon.SCARD_STATE_UNAWARE;
27
+ const SCARD_STATE_IGNORE = addon.SCARD_STATE_IGNORE;
28
+ const SCARD_STATE_CHANGED = addon.SCARD_STATE_CHANGED;
29
+ const SCARD_STATE_UNKNOWN = addon.SCARD_STATE_UNKNOWN;
30
+ const SCARD_STATE_UNAVAILABLE = addon.SCARD_STATE_UNAVAILABLE;
31
+ const SCARD_STATE_EMPTY = addon.SCARD_STATE_EMPTY;
32
+ const SCARD_STATE_PRESENT = addon.SCARD_STATE_PRESENT;
33
+ const SCARD_STATE_ATRMATCH = addon.SCARD_STATE_ATRMATCH;
34
+ const SCARD_STATE_EXCLUSIVE = addon.SCARD_STATE_EXCLUSIVE;
35
+ const SCARD_STATE_INUSE = addon.SCARD_STATE_INUSE;
36
+ const SCARD_STATE_MUTE = addon.SCARD_STATE_MUTE;
37
+
38
+ // Import Devices class
39
+ const { Devices } = require('./devices');
40
+
41
+ // Import error classes
42
+ const { PCSCError, CardRemovedError, TimeoutError } = require('./errors');
16
43
 
17
44
  module.exports = {
18
- Iso7816Application: _Iso7816Application["default"],
19
- CommandApdu: _CommandApdu["default"],
20
- ResponseApdu: _ResponseApdu["default"],
21
- Devices: _Devices["default"],
22
- Device: _Device["default"],
23
- Card: _Card["default"]
24
- };
45
+ // Classes
46
+ Context,
47
+ Reader,
48
+ Card,
49
+ Devices,
50
+ ReaderMonitor,
51
+
52
+ // Error classes
53
+ PCSCError,
54
+ CardRemovedError,
55
+ TimeoutError,
56
+
57
+ // Share modes
58
+ SCARD_SHARE_EXCLUSIVE,
59
+ SCARD_SHARE_SHARED,
60
+ SCARD_SHARE_DIRECT,
61
+
62
+ // Protocols
63
+ SCARD_PROTOCOL_T0,
64
+ SCARD_PROTOCOL_T1,
65
+ SCARD_PROTOCOL_RAW,
66
+ SCARD_PROTOCOL_UNDEFINED,
67
+
68
+ // Disposition
69
+ SCARD_LEAVE_CARD,
70
+ SCARD_RESET_CARD,
71
+ SCARD_UNPOWER_CARD,
72
+ SCARD_EJECT_CARD,
73
+
74
+ // State flags
75
+ SCARD_STATE_UNAWARE,
76
+ SCARD_STATE_IGNORE,
77
+ SCARD_STATE_CHANGED,
78
+ SCARD_STATE_UNKNOWN,
79
+ SCARD_STATE_UNAVAILABLE,
80
+ SCARD_STATE_EMPTY,
81
+ SCARD_STATE_PRESENT,
82
+ SCARD_STATE_ATRMATCH,
83
+ SCARD_STATE_EXCLUSIVE,
84
+ SCARD_STATE_INUSE,
85
+ SCARD_STATE_MUTE,
86
+ };
package/package.json CHANGED
@@ -1,41 +1,58 @@
1
1
  {
2
- "name": "smartcard",
3
- "version": "1.0.46",
4
- "author": "tomkp <tom@tomkp.com>",
5
- "keywords": [
6
- "pcsc",
7
- "smartcard",
8
- "smart-card",
9
- "iso7816",
10
- "iso-7816",
11
- "emv",
12
- "es6"
13
- ],
14
- "license": "MIT",
15
- "repository": {
16
- "type": "git",
17
- "url": "https://github.com/tomkp/smartcard.git"
18
- },
19
- "bugs": {
20
- "url": "https://github.com/tomkp/smartcard/issues"
21
- },
22
- "homepage": "https://github.com/tomkp/smartcard",
23
- "main": "lib/index.js",
24
- "scripts": {
25
- "compile": "babel -d lib/ src/",
26
- "compile:watch": "babel -w -d lib/ src/",
27
- "release:patch": "npm run compile && npm version patch && git push && yarn publish",
28
- "prettier": "prettier --write \"{src,demo}/**/*.{js,ts}\""
29
- },
30
- "dependencies": {
31
- "@pokusew/pcsclite": "^0.6.0",
32
- "hexify": "^1.0.4",
33
- "pino": "^6.11.1"
34
- },
35
- "devDependencies": {
36
- "@babel/cli": "^7.12.16",
37
- "@babel/core": "^7.12.16",
38
- "@babel/preset-env": "^7.12.16",
39
- "prettier": "^2.2.1"
40
- }
2
+ "name": "smartcard",
3
+ "version": "2.0.0",
4
+ "description": "PC/SC bindings for Node.js using N-API - ABI stable across Node.js versions",
5
+ "author": "Tom KP",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/tomkp/smartcard.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/tomkp/smartcard/issues"
13
+ },
14
+ "homepage": "https://github.com/tomkp/smartcard#readme",
15
+ "main": "lib/index.js",
16
+ "types": "lib/index.d.ts",
17
+ "gypfile": true,
18
+ "scripts": {
19
+ "install": "node-gyp rebuild",
20
+ "build": "node-gyp rebuild",
21
+ "rebuild": "node-gyp rebuild",
22
+ "test": "node test/test.js"
23
+ },
24
+ "files": [
25
+ "lib/",
26
+ "src/",
27
+ "binding.gyp",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "dependencies": {
32
+ "node-addon-api": "^7.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "node-gyp": "^10.0.0"
36
+ },
37
+ "engines": {
38
+ "node": ">=12.0.0"
39
+ },
40
+ "os": [
41
+ "darwin",
42
+ "linux",
43
+ "win32"
44
+ ],
45
+ "keywords": [
46
+ "pcsc",
47
+ "pcsclite",
48
+ "smartcard",
49
+ "smart-card",
50
+ "nfc",
51
+ "rfid",
52
+ "contactless",
53
+ "napi",
54
+ "node-addon-api",
55
+ "acr122",
56
+ "card-reader"
57
+ ]
41
58
  }
package/src/addon.cpp ADDED
@@ -0,0 +1,54 @@
1
+ #include <napi.h>
2
+ #include "pcsc_context.h"
3
+ #include "pcsc_reader.h"
4
+ #include "pcsc_card.h"
5
+ #include "reader_monitor.h"
6
+ #include "platform/pcsc.h"
7
+
8
+ // Export PC/SC constants
9
+ void ExportConstants(Napi::Env env, Napi::Object exports) {
10
+ // Share modes
11
+ exports.Set("SCARD_SHARE_EXCLUSIVE", Napi::Number::New(env, SCARD_SHARE_EXCLUSIVE));
12
+ exports.Set("SCARD_SHARE_SHARED", Napi::Number::New(env, SCARD_SHARE_SHARED));
13
+ exports.Set("SCARD_SHARE_DIRECT", Napi::Number::New(env, SCARD_SHARE_DIRECT));
14
+
15
+ // Protocols
16
+ exports.Set("SCARD_PROTOCOL_T0", Napi::Number::New(env, SCARD_PROTOCOL_T0));
17
+ exports.Set("SCARD_PROTOCOL_T1", Napi::Number::New(env, SCARD_PROTOCOL_T1));
18
+ exports.Set("SCARD_PROTOCOL_RAW", Napi::Number::New(env, SCARD_PROTOCOL_RAW));
19
+ exports.Set("SCARD_PROTOCOL_UNDEFINED", Napi::Number::New(env, SCARD_PROTOCOL_UNDEFINED));
20
+
21
+ // Disposition
22
+ exports.Set("SCARD_LEAVE_CARD", Napi::Number::New(env, SCARD_LEAVE_CARD));
23
+ exports.Set("SCARD_RESET_CARD", Napi::Number::New(env, SCARD_RESET_CARD));
24
+ exports.Set("SCARD_UNPOWER_CARD", Napi::Number::New(env, SCARD_UNPOWER_CARD));
25
+ exports.Set("SCARD_EJECT_CARD", Napi::Number::New(env, SCARD_EJECT_CARD));
26
+
27
+ // State flags
28
+ exports.Set("SCARD_STATE_UNAWARE", Napi::Number::New(env, SCARD_STATE_UNAWARE));
29
+ exports.Set("SCARD_STATE_IGNORE", Napi::Number::New(env, SCARD_STATE_IGNORE));
30
+ exports.Set("SCARD_STATE_CHANGED", Napi::Number::New(env, SCARD_STATE_CHANGED));
31
+ exports.Set("SCARD_STATE_UNKNOWN", Napi::Number::New(env, SCARD_STATE_UNKNOWN));
32
+ exports.Set("SCARD_STATE_UNAVAILABLE", Napi::Number::New(env, SCARD_STATE_UNAVAILABLE));
33
+ exports.Set("SCARD_STATE_EMPTY", Napi::Number::New(env, SCARD_STATE_EMPTY));
34
+ exports.Set("SCARD_STATE_PRESENT", Napi::Number::New(env, SCARD_STATE_PRESENT));
35
+ exports.Set("SCARD_STATE_ATRMATCH", Napi::Number::New(env, SCARD_STATE_ATRMATCH));
36
+ exports.Set("SCARD_STATE_EXCLUSIVE", Napi::Number::New(env, SCARD_STATE_EXCLUSIVE));
37
+ exports.Set("SCARD_STATE_INUSE", Napi::Number::New(env, SCARD_STATE_INUSE));
38
+ exports.Set("SCARD_STATE_MUTE", Napi::Number::New(env, SCARD_STATE_MUTE));
39
+ }
40
+
41
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
42
+ // Initialize wrapper classes
43
+ PCSCContext::Init(env, exports);
44
+ PCSCReader::Init(env, exports);
45
+ PCSCCard::Init(env, exports);
46
+ ReaderMonitor::Init(env, exports);
47
+
48
+ // Export constants
49
+ ExportConstants(env, exports);
50
+
51
+ return exports;
52
+ }
53
+
54
+ NODE_API_MODULE(smartcard_napi, Init)