-
v4.0.0
StableAll checks were successfulPublish (NPM) / publish (release) Successful in 9sPublish (JSR) / publish (release) Successful in 13sPublish (Maven) / publish (release) Successful in 1m0sci-orchestrator / provision running on cpx42 in fsn1Publish (crates.io) / publish (release) Successful in 23sreleased this
2026-10-04 15:06:17 +02:00 | 0 commits to main since this releasev4.0.0
JavaScript, TypeScript lib
decodeCardreads Lazo cards with a 4-byte UID. A 4K chip can carry a 4-byte UID, so the chip and the UID length are detected separately from block 0, andCard.uidis 4 or 7 bytes on a 4K chipCardType.LazoTopUp→CardType.LazoTopUp371FCardType.LazoTopUp375Fadded for the second Lazo card type,0D375FCard.journeySummary→JourneySummarySlot, either{ state: "read", summary }or{ state: "unknown", error }, so a block 10 that does not decode no longer throws away the whole card.decodeJourneySummarystill throws on itJourneySummary.transfersLeft→paidMarker, since byte [13] is only known as0x63after a paid journey and0x62after a free transfer- The block 0 error names the bytes it read
Rust lib
Card::decodereads Lazo cards with a 4-byte UID. A 4K chip can carry a 4-byte UID, soUid::decodereturns(Uid, Chip)andUid::chipis removedCard::chip,CardType::chip()andChip::sectors()addedCardType::LazoTopUp→CardType::LazoTopUp371FCardType::LazoTopUp375Fadded for the second Lazo card type,0D375F, soCardType::ALLis now[Self; 5]Card::journey_summary→Option<Result<JourneySummary>>, so a block 10 that does not decode no longer throws away the whole card.JourneySummary::decodestill returns the errorJourneySummary::transfers_left→paid_marker, since byte [13] is only known as0x63after a paid journey and0x62after a free transferError::Sak→Error::Sak { byte_5, byte_7 }, the bytes it read
Java, Kotlin lib
Card.decodereads Lazo cards with a 4-byte UID. A 4K chip can carry a 4-byte UID, soUid.of(byte[])→Uid.of(byte[], Chip), andChip.uidLength()andChip.ofUidLength(int)are removedCardType.LAZO_TOP_UP→CardType.LAZO_TOP_UP_371FCardType.LAZO_TOP_UP_375Fadded for the second Lazo card type,0D375FCard.journeySummary()→Optional<JourneySummarySlot>, eitherReadorUnknowncarrying theCardFormatException, so a block 10 that does not decode no longer throws away the whole card.JourneySummary.decodestill throws on it- The
JourneySummarycomponenttransfersLeft→paidMarker, since byte [13] is only known as0x63after a paid journey and0x62after a free transfer - The block 0 error names the bytes it read
-
v3.0.0
StableSome checks failedci-orchestrator / provision running on cx23 in hel1Publish (NPM) / publish (release) Successful in 13sPublish (JSR) / publish (release) Successful in 23sPublish (crates.io) / publish (release) Failing after 37sPublish (Maven) / publish (release) Failing after 51sreleased this
2026-09-11 11:17:43 +02:00 | 6 commits to main since this releasev3.0.0
JavaScript, TypeScript lib
Transaction.cardTypeandJourneySummary.cardType→productId, a raw byte. Byte [00] of a transaction and byte [07] of the journey summary hold the product that paid, not the first byte of the card typecardTypeByteandcardTypeFromByteare removed:06is a product id belonging to no card type, and the two personal card types share a first byteCardType.AvanzaPersonalUnlimited→CardType.AvanzaPersonalCardType.AvanzaPersonalAbonoadded for the second personal Avanza card type,0A98DAisPersonalCardType(type)addedisTransfer(t)testsnetworkFlag, so it isfalseon every personal card recordarchiveBlock(sequence)returnsnumber | undefined, since a top up can carry0x21
Rust lib
Transaction::card_typeandJourneySummary::card_type→product_id: u8, a raw byte. Byte [00] of a transaction and byte [07] of the journey summary hold the product that paid, not the first byte of the card typeCardType::byte,CardType::from_byteandError::UnknownCardTypeByteare removed:06is a product id belonging to no card type, and the two personal card types share a first byteCardType::AvanzaPersonalUnlimited→CardType::AvanzaPersonalCardType::AvanzaPersonalAbonoadded for the second personal Avanza card type,0A98DA, soCardType::ALLis now[Self; 4]CardType::is_personal()addedTransaction::is_transfer()testsnetwork_flag, so it isfalseon every personal card record
Java, Kotlin lib
- The
TransactionandJourneySummarycomponentcardType→int productId, so the accessor isproductId()and the builder methodTransactionBuilder.productId(int). Byte [00] of a transaction and byte [07] of the journey summary hold the product that paid, not the first byte of the card type CardType.firstByte()andCardType.ofFirstByte(int)are removed:0x06is a product id belonging to no card type, and the two personal card types share a first byteCardType.AVANZA_PERSONAL_UNLIMITED→CardType.AVANZA_PERSONALCardType.AVANZA_PERSONAL_ABONOadded for the second personal Avanza card type,0A98DACardType.isPersonal()addedTransaction.isTransfer()tests the network flag, so it is false on every personal card record
-
v2.0.0
StableAll checks were successfulci-orchestrator / provision running on cx23 in fsn1Publish (NPM) / publish (release) Successful in 14sPublish (JSR) / publish (release) Successful in 21sPublish (crates.io) / publish (release) Successful in 38sPublish (Maven) / publish (release) Successful in 1m41sreleased this
2026-08-27 19:34:09 +02:00 | 10 commits to main since this releasev2.0.0
JavaScript, TypeScript lib
isFreeTransferis removed and replaced by three predicates:isFree(transaction)— the journey cost nothingisTransfer(transaction)— is it the free transferisCheckOut(transaction)— a check-out at a gated station
Transaction.runCounteris renamed todutyTrip. Byte [09] is the trip index within the vehicle's daily duty, not a free-running counter. This affects bothdecodeTransactionoutput andencodeTransactioninput.isFreeTransfer(t)→isFree(t)orisTransfer(t)t.runCounter→t.dutyTrip
Rust lib
Transaction::is_free_transferis removed and replaced by threeconst fnpredicates:is_free()— the journey cost nothingis_transfer()— is it the free transferis_check_out()— a check-out at a gated station
Transaction::run_counteris renamed toduty_trip. Byte [09] is the trip index within the vehicle's daily duty, not a free-running counter. This affects struct literals, decoding and encoding.Upgrading:
t.is_free_transfer()→t.is_free()ort.is_transfer()t.run_counter→t.duty_trip
Java, Kotlin lib
Transaction.isFreeTransfer()is removed and replaced by three methods:isFree()— the journey cost nothingisTransfer()— the one free transfer a balance card earns after a paid rideisCheckOut()— a check-out at a gated station
The
Transactionrecord componentrunCounteris renamed todutyTrip, so the accessor is nowdutyTrip()and the builder methodTransactionBuilder.dutyTrip(int). Byte [09] is the trip index within the vehicle's daily duty, not a free-running counter.Upgrading:
t.isFreeTransfer()→t.isFree()ort.isTransfer()t.runCounter()→t.dutyTrip().runCounter(n)→.dutyTrip(n)on the builder
-
v1.0.3
StableAll checks were successfulci-orchestrator / provision running on cx23 in fsn1Publish (NPM) / publish (release) Successful in 16sPublish (JSR) / publish (release) Successful in 24sPublish (crates.io) / publish (release) Successful in 40sPublish (Maven) / publish (release) Successful in 1m35sreleased this
2026-08-27 15:36:03 +02:00 | 13 commits to main since this releasev1.0.3
JavaScript, TypeScript lib
- Update README
Rust lib
- Update README
Java, Kotlin lib
- Add library
-
v1.0.2
Stablereleased this
2026-08-26 19:16:59 +02:00 | 23 commits to main since this release -
v1.0.1
Stablereleased this
2026-08-26 18:05:49 +02:00 | 29 commits to main since this releasev1.0.1
JavaScript, TypeScript lib
- Fix 4K cards detection
- Add ".ts" to support Node.js >= 25.0
Rust lib
No changes.
-
v1.0.0
Stablereleased this
2026-08-26 02:41:32 +02:00 | 38 commits to main since this releasev1.0.0
JavaScript, TypeScript lib
- Add library
Rust lib
- Add library