• v4.0.0 48302b235c

    v4.0.0
    All checks were successful
    Publish (NPM) / publish (release) Successful in 9s
    Publish (JSR) / publish (release) Successful in 13s
    Publish (Maven) / publish (release) Successful in 1m0s
    ci-orchestrator / provision running on cpx42 in fsn1
    Publish (crates.io) / publish (release) Successful in 23s
    Stable

    hloth released this 2026-10-04 15:06:17 +02:00 | 0 commits to main since this release

    Signed by hloth
    GPG key ID: 299E9A450132A28C

    v4.0.0

    Full changelog

    JavaScript, TypeScript lib

    • decodeCard reads 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, and Card.uid is 4 or 7 bytes on a 4K chip
    • CardType.LazoTopUp → CardType.LazoTopUp371F
    • CardType.LazoTopUp375F added for the second Lazo card type, 0D375F
    • Card.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. decodeJourneySummary still throws on it
    • JourneySummary.transfersLeft → paidMarker, since byte [13] is only known as 0x63 after a paid journey and 0x62 after a free transfer
    • The block 0 error names the bytes it read

    Rust lib

    • Card::decode reads Lazo cards with a 4-byte UID. A 4K chip can carry a 4-byte UID, so Uid::decode returns (Uid, Chip) and Uid::chip is removed
    • Card::chip, CardType::chip() and Chip::sectors() added
    • CardType::LazoTopUp → CardType::LazoTopUp371F
    • CardType::LazoTopUp375F added for the second Lazo card type, 0D375F, so CardType::ALL is 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::decode still returns the error
    • JourneySummary::transfers_left → paid_marker, since byte [13] is only known as 0x63 after a paid journey and 0x62 after a free transfer
    • Error::Sak → Error::Sak { byte_5, byte_7 }, the bytes it read

    Java, Kotlin lib

    • Card.decode reads Lazo cards with a 4-byte UID. A 4K chip can carry a 4-byte UID, so Uid.of(byte[]) → Uid.of(byte[], Chip), and Chip.uidLength() and Chip.ofUidLength(int) are removed
    • CardType.LAZO_TOP_UP → CardType.LAZO_TOP_UP_371F
    • CardType.LAZO_TOP_UP_375F added for the second Lazo card type, 0D375F
    • Card.journeySummary() → Optional<JourneySummarySlot>, either Read or Unknown carrying the CardFormatException, so a block 10 that does not decode no longer throws away the whole card. JourneySummary.decode still throws on it
    • The JourneySummary component transfersLeft → paidMarker, since byte [13] is only known as 0x63 after a paid journey and 0x62 after a free transfer
    • The block 0 error names the bytes it read
  • v3.0.0 2648b25939

    v3.0.0
    Some checks failed
    ci-orchestrator / provision running on cx23 in hel1
    Publish (NPM) / publish (release) Successful in 13s
    Publish (JSR) / publish (release) Successful in 23s
    Publish (crates.io) / publish (release) Failing after 37s
    Publish (Maven) / publish (release) Failing after 51s
    Stable

    hloth released this 2026-09-11 11:17:43 +02:00 | 6 commits to main since this release

    v3.0.0

    Full changelog

    JavaScript, TypeScript lib

    • Transaction.cardType and JourneySummary.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 type
    • cardTypeByte and cardTypeFromByte are removed: 06 is a product id belonging to no card type, and the two personal card types share a first byte
    • CardType.AvanzaPersonalUnlimited → CardType.AvanzaPersonal
    • CardType.AvanzaPersonalAbono added for the second personal Avanza card type, 0A98DA
    • isPersonalCardType(type) added
    • isTransfer(t) tests networkFlag, so it is false on every personal card record
    • archiveBlock(sequence) returns number | undefined, since a top up can carry 0x21

    Rust lib

    • Transaction::card_type and JourneySummary::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 type
    • CardType::byte, CardType::from_byte and Error::UnknownCardTypeByte are removed: 06 is a product id belonging to no card type, and the two personal card types share a first byte
    • CardType::AvanzaPersonalUnlimited → CardType::AvanzaPersonal
    • CardType::AvanzaPersonalAbono added for the second personal Avanza card type, 0A98DA, so CardType::ALL is now [Self; 4]
    • CardType::is_personal() added
    • Transaction::is_transfer() tests network_flag, so it is false on every personal card record

    Java, Kotlin lib

    • The Transaction and JourneySummary component cardType → int productId, so the accessor is productId() and the builder method TransactionBuilder.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() and CardType.ofFirstByte(int) are removed: 0x06 is a product id belonging to no card type, and the two personal card types share a first byte
    • CardType.AVANZA_PERSONAL_UNLIMITED → CardType.AVANZA_PERSONAL
    • CardType.AVANZA_PERSONAL_ABONO added for the second personal Avanza card type, 0A98DA
    • CardType.isPersonal() added
    • Transaction.isTransfer() tests the network flag, so it is false on every personal card record
  • v2.0.0 f57ebad3f0

    v2.0.0
    All checks were successful
    ci-orchestrator / provision running on cx23 in fsn1
    Publish (NPM) / publish (release) Successful in 14s
    Publish (JSR) / publish (release) Successful in 21s
    Publish (crates.io) / publish (release) Successful in 38s
    Publish (Maven) / publish (release) Successful in 1m41s
    Stable

    hloth released this 2026-08-27 19:34:09 +02:00 | 10 commits to main since this release

    v2.0.0

    Full changelog

    JavaScript, TypeScript lib

    isFreeTransfer is removed and replaced by three predicates:

    • isFree(transaction) — the journey cost nothing
    • isTransfer(transaction) — is it the free transfer
    • isCheckOut(transaction) — a check-out at a gated station

    Transaction.runCounter is renamed to dutyTrip. Byte [09] is the trip index within the vehicle's daily duty, not a free-running counter. This affects both decodeTransaction output and encodeTransaction input.

    • isFreeTransfer(t) → isFree(t) or isTransfer(t)
    • t.runCounter → t.dutyTrip

    Rust lib

    Transaction::is_free_transfer is removed and replaced by three const fn predicates:

    • is_free() — the journey cost nothing
    • is_transfer() — is it the free transfer
    • is_check_out() — a check-out at a gated station

    Transaction::run_counter is renamed to duty_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() or t.is_transfer()
    • t.run_counter → t.duty_trip

    Java, Kotlin lib

    Transaction.isFreeTransfer() is removed and replaced by three methods:

    • isFree() — the journey cost nothing
    • isTransfer() — the one free transfer a balance card earns after a paid ride
    • isCheckOut() — a check-out at a gated station

    The Transaction record component runCounter is renamed to dutyTrip, so the accessor is now dutyTrip() and the builder method TransactionBuilder.dutyTrip(int). Byte [09] is the trip index within the vehicle's daily duty, not a free-running counter.

    Upgrading:

    • t.isFreeTransfer() → t.isFree() or t.isTransfer()
    • t.runCounter() → t.dutyTrip()
    • .runCounter(n) → .dutyTrip(n) on the builder
  • v1.0.3 e93ab9007a

    v1.0.3
    All checks were successful
    ci-orchestrator / provision running on cx23 in fsn1
    Publish (NPM) / publish (release) Successful in 16s
    Publish (JSR) / publish (release) Successful in 24s
    Publish (crates.io) / publish (release) Successful in 40s
    Publish (Maven) / publish (release) Successful in 1m35s
    Stable

    hloth released this 2026-08-27 15:36:03 +02:00 | 13 commits to main since this release

    v1.0.3

    Full changelog

    JavaScript, TypeScript lib

    • Update README

    Rust lib

    • Update README

    Java, Kotlin lib

    • Add library
  • v1.0.2 709503117a

    v1.0.2
    All checks were successful
    ci-orchestrator / provision running on cx23 in fsn1
    Publish (NPM) / publish (release) Successful in 15s
    Publish (JSR) / publish (release) Successful in 19s
    Publish (crates.io) / publish (release) Successful in 38s
    Stable

    hloth released this 2026-08-26 19:16:59 +02:00 | 23 commits to main since this release

    v1.0.2

    Full changelog

    JavaScript, TypeScript lib

    • Update README

    Rust lib

    • Update README
  • v1.0.1 22f54cf42c

    v1.0.1
    Some checks failed
    Publish (JSR) / publish (release) Successful in 18s
    Publish (NPM) / publish (release) Successful in 17s
    Publish (crates.io) / publish (release) Failing after 23s
    ci-orchestrator / provision running on cx23 in fsn1
    Stable

    hloth released this 2026-08-26 18:05:49 +02:00 | 29 commits to main since this release

    v1.0.1

    Full changelog

    JavaScript, TypeScript lib

    • Fix 4K cards detection
    • Add ".ts" to support Node.js >= 25.0

    Rust lib

    No changes.

  • v1.0.0 7fa68fc7bc

    v1.0.0
    Some checks failed
    ci-orchestrator / provision running on cx23 in fsn1
    Publish (NPM) / publish (release) Successful in 9s
    Publish (JSR) / publish (release) Failing after 9s
    Stable

    hloth released this 2026-08-26 02:41:32 +02:00 | 38 commits to main since this release

    v1.0.0

    JavaScript, TypeScript lib

    • Add library

    Rust lib

    • Add library