Uint8Array, built on a single
transceive primitive. There is no native module and no React Native import — a
lint rule enforces that — so these layers are unit tested at 100% branch coverage
rather than on a device, and they work in Node and on the web too.
That is the point of the split: the native side does one thing, which is move bytes
to and from a tag, and everything that can be reasoned about in software lives here
where it can be tested properly.
The transport
Every function takes a transport: a function from bytes to bytes. A narrowed tag gives you one directly.A transport is a plain function, which is what makes all of this testable without
hardware: pass
async (bytes) => cannedResponse and you have a fake card. It is
also why these helpers work against a reader on a laptop, or against captured
bytes, with no changes.- ISO 7816
- ISO 15693
- FeliCa
- NTAG / Ultralight
For DESFire, JavaCard applets, transit cards, EMV-adjacent work — anything where
you select an application by AID and exchange APDUs.For command data larger than one APDU, Also available:
Building commands
selectByName and friends return a CommandApdu; they do not send one. That
separation is what lets you log, cache or assert on a command before it goes out.Or write one out.
le counts bytes, so 256 is 256 — the encoding quirk where
0x00 means 256 is handled for you:Sending them
sendApdu handles 61xx and 6Cxx transparently, so the status word you get back
is the card’s actual verdict rather than a protocol detail, and data is everything
the card had to say rather than the first frame of it.boolean
default:"true"
Follows a
61xx with GET RESPONSE, concatenating the parts.boolean
default:"true"
Repeats the command with the corrected length after a
6Cxx.number
default:"32"
How many follow-up exchanges to allow. A card that answers
61xx forever would otherwise loop
until the session times out with nothing to show for it.sendApduChained splits it and sets the
chaining bit for you:Reading the answer
encodeCommandApdu, decodeResponseApdu, and the constants
SW_SUCCESS, CLA_CHAINING, SHORT_MAX_LC, SHORT_MAX_LE, EXTENDED_MAX_LC and
EXTENDED_MAX_LE.Namespaced access
Each protocol is also exported as a namespace, for when a bare name would be ambiguous in your own code:Without a device
None of this needs a phone. A transport is a function, so a test is a function:Next
Card emulation
The other side of ISO 7816: answering a terminal.
Error reference
transceiveFailed, transceiveTooLong, authenticationFailed.