npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hlf-core/transport

v3.2.11

Published

TypeScript library for Hyperledger Fabric client transport: command sending, block and event parsing, batch delivery and chaincode error handling

Readme

English · Русский

@hlf-core/transport

TypeScript client transport library for Hyperledger Fabric: sending commands, parsing blocks, and receiving events

npm version Docs License: ISC

Full documentation: https://hlf.gnosisai.ru/docs/packages/transport

The client side of the transport: connecting to the network, sending commands to chaincode, receiving responses, and parsing blocks and events. It implements the @ts-core/common command model on top of fabric-network, so application code works with the transport the same way regardless of whether it talks to a blockchain or to an ordinary service.

The paired package on the chaincode side is @hlf-core/transport-chaincode; the shared protocol is @hlf-core/transport-common.

Table of Contents

Overview

The transport extends TransportImpl from @ts-core/common, meaning it provides the familiar send and sendListen methods for commands. The difference from an ordinary transport is that delivery goes through Hyperledger Fabric: a command is packed into an envelope, sent via a single contract method, and — depending on the isReadonly flag — executed either as a read-only query or as a transaction with endorsement.

The reverse direction works differently. Chaincode cannot "respond" outside of a transaction, so events arrive not through the transport but from blocks: the transport subscribes to the network's blocks and parses their contents.

Key Features

  • Sending commands — with automatic selection between a query and a transaction based on the isReadonly flag
  • Connection with reconnection — configurable delay and number of attempts
  • Chaincode error parsing — the domain error is extracted from the Fabric response and restored as an ExtendedError with its code and details
  • Block parsing — transactions, events, validation codes, and the link between events and the transactions that produced them
  • Batch mode — commands are accumulated by the chaincode and executed in a single transaction, with responses parsed from the block
  • Transport factory — reuse of connections by identifier

Installation

npm install @hlf-core/transport

Dependencies

{
    "@hlf-core/api": "~3.1.4",
    "@hlf-core/transport-common": "~3.0.15"
}

@ts-core/common, fabric-network, and fabric-common are pulled in transitively (through @hlf-core/api).

Quick Start

import { TransportFabric } from '@hlf-core/transport';

let transport = new TransportFabric(logger, {
    fabricIdentity: 'user',
    fabricNetworkName: 'mychannel',
    fabricChaincodeName: 'mychaincode',
    fabricConnectionSettingsPath: './connection.json',
    fabricIdentityPrivateKey: privateKey,
    fabricIdentityCertificate: certificate,

    reconnectDelay: 1000,
    reconnectMaxAttempts: 5,
    isExitApplicationOnDisconnect: false
});

await transport.connect();

// command with a reply
let user = await transport.sendListen(new UserGetCommand(uid));

// command without waiting for a reply
transport.send(new UserAddCommand({ name: 'Ivan', publicKey }));

How It Works

Connection and Reconnection

connect() applies default values and starts the connection loop:

if (!_.isNumber(this.settings.reconnectDelay)) { this.settings.reconnectDelay = DateUtil.MILISECONDS_SECOND; }
if (!_.isNumber(this.settings.reconnectMaxAttempts)) { this.settings.reconnectMaxAttempts = 0; }
if (!_.isBoolean(this.settings.isExitApplicationOnDisconnect)) { this.settings.isExitApplicationOnDisconnect = true; }

Attempts are repeated with a reconnectDelay delay until their count exceeds reconnectMaxAttempts. By default there are no retries: reconnectMaxAttempts is zero, so the very first failure leads to a disconnect.

On a successful connection the transport subscribes to the network's blocks and to the contract's events, after which the connect() promise resolves.

Important: isExitApplicationOnDisconnect is enabled by default, and on a disconnect the transport terminates the process via process.exit(1). This is convenient for services managed by an orchestrator that will restart the container, but unexpected in other scenarios. See the Important Notes section for details.

Sending a Command

protected async commandRequestExecute(command, options, isNeedReply) {
    let payload = this.createRequestPayload(command, options, isNeedReply);
    TransportFabricRequestPayload.clear(payload);

    let response = await this.transactionSend(this.api.contract.createTransaction(TRANSPORT_FABRIC_METHOD), command, payload);
    if (isNeedReply && this.isCommandAsync(command)) {
        this.responseMessageReceived(command.id, response);
    }
}

The steps: assemble the envelope, strip default values out of it, send it via the single contract method, and parse the response.

The sending method is chosen based on the command's flag:

let method = payload.isReadonly ? transaction.evaluate : transaction.submit;

evaluate executes the command on a peer without creating a transaction — nothing ends up in the ledger. submit goes through the full endorsement and commit cycle.

The flag is taken from the command itself, and both names are supported — the historical isQuery and the current isReadonly:

protected isCommandReadonly<U>(command: ITransportCommand<U>): boolean {
    if (ObjectUtil.hasOwnProperty(command, 'isQuery')) { return command['isQuery'] === true; }
    if (ObjectUtil.hasOwnProperty(command, 'isReadonly')) { return command['isReadonly'] === true; }
    return false;
}

Chaincode Errors

Fabric returns a chaincode error wrapped in its own structure and prefixed with text. The transport parses it and restores the original domain error:

protected parseChaincodeError(error: any): ExtendedError {
    let message = error.message.replace('error in simulation: transaction returned with failure:', '').trim();
    if (!ObjectUtil.isJSON(message)) { return null; }

    let response = TransformUtil.toClass(TransportFabricResponsePayload, TransformUtil.toJSON(message));
    if (!ExtendedError.instanceOf(response.response)) { return null; }

    let item = ExtendedError.create(response.response);
    item.stack = null;
    return item;
}

Thanks to this, application code catches the same error with the same code that was thrown inside the chaincode, rather than a string of transaction text. The stack is nulled out — it would refer to the client and would be misleading.

The error source is searched for in sequence in response, responses[0].response, endorsements[0], and in the error itself when the status is 500 — the shape of the response depends on the stage at which Fabric aborted execution.

Blocks and Events

The transport is one-way: commands flow only from the client to the chaincode. An attempt to send an event or a response in the reverse direction is explicitly forbidden:

protected eventRequestExecute<U>(event: ITransportEvent<U>, options?: void): Promise<void> {
    throw new ExtendedError(`Method doesn't supported`);
}

Events arrive from blocks. TransportFabricBlockParser parses a block into a structure of transactions and events, filling in validation codes from the block's metadata along the way and linking events to transactions by hash:

public static checkEventsTransaction(events, transactions): void {
    let map = new Map();
    transactions.forEach(item => map.set(item.hash, item));

    for (let event of events) {
        let item = map.get(event.transactionHash);
        event.requestId = item.requestId;
        event.transactionValidationCode = item.validationCode;
    }
}

This makes it possible to learn from an event which command produced it (requestId) and whether the transaction was accepted by the network (transactionValidationCode) — the event is present in the block even for a rejected transaction.

Transport events are distinguished from the rest by name: everything published under transportFabricEvent is parsed as a "transaction hash → list of events" map, and everything else as a single chaincode event.

Empty hooks are provided for subclasses:

protected async blockEventCallback(event: IFabricBlock): Promise<void> { }
protected async contractEventCallback(event: ContractEvent): Promise<void> { }

Batch Mode

TransportFabricBatch changes the delivery scheme for state-changing commands. The command is sent, but the response is not awaited immediately: the chaincode accumulates such commands and executes them in a single transaction. The responses come back later — from the block.

protected async commandRequestExecute(command, options, isNeedReply) {
    if (this.isCommandReadonly(command)) {
        return super.commandRequestExecute(command, options, isNeedReply);   // readonly — as usual
    }
    let payload = this.createRequestPayload(command, options, isNeedReply);
    TransportFabricRequestPayload.clear(payload);
    return this.transactionSend(...);   // the reply will come from the block
}

When a block arrives, the transport looks in it for a successful batch transaction and parses its response as a "hash → response" map, after which it resolves the promises of the waiting commands:

let payload = TransformUtil.toClass(TransportFabricResponsePayload, batch.response);
for (let hash in payload.response) {
    let item = TransformUtil.toClass(TransportFabricResponsePayload, payload.response[hash]);
    this.responseMessageReceived(item.id, TransformUtil.fromClassBuffer(item));
}

Read commands do not end up in a batch — they are still executed immediately via evaluate.

API Documentation

TransportFabric

class TransportFabric<T extends ITransportFabricConnectionSettings> extends TransportImpl<T, ITransportFabricCommandOptions>

| Method | Purpose | |---|---| | connect(): Promise<void> | connect to the network with reconnection; a repeated call returns the same promise | | disconnect(error?): void | unsubscribe from blocks and events, tear down the connection | | destroy(): void | destroy the transport; internally calls disconnect() | | isConnected: boolean | connection state | | api: FabricApiClient | access to the Fabric client for direct operations |

Commands are sent via the inherited send and sendListen from TransportImpl.

Methods to override in subclasses:

| Method | Purpose | |---|---| | blockEventCallback(block) | called for every network block | | contractEventCallback(event) | called for every contract event | | isCommandReadonly(command) | determines the sending method | | parseError(error) / parseChaincodeError(error) | parse Fabric errors |

ITransportFabricConnectionSettings

export interface ITransportFabricConnectionSettings extends IFabricConnectionSettings, ITransportSettings {
    reconnectDelay?: number;
    reconnectMaxAttempts?: number;
    isExitApplicationOnDisconnect?: boolean;
}

| Parameter | Default | Purpose | |---|---|---| | reconnectDelay | 1000 ms | delay between connection attempts | | reconnectMaxAttempts | 0 | maximum number of retries; zero means no retries | | isExitApplicationOnDisconnect | true | terminate the process on a disconnect |

The remaining connection parameters (identity, channel, chaincode name, certificates) are inherited from IFabricConnectionSettings of the @hlf-core/api package.

TransportFabricBlockParser

class TransportFabricBlockParser<U extends ITransportFabricTransaction, V extends ITransportFabricEvent, T extends ITransportFabricBlock<U, V>>

| Method | Purpose | |---|---| | parse(block): Promise<T> | parse a block into transactions and events | | parseTransaction(data): U | parse a single transaction with its validation code | | checkEventsTransaction(events, transactions) | link events to transactions by hash | | isTransactionValid(item) | the validation code equals VALID | | isTransactionError(item) | the transaction's response is an error | | isTransactionSucceed(item) | the transaction is valid and contains no error |

Only transport-related content is parsed: a transaction is taken into account if the transportFabricExecute method was called with exactly two arguments.

The structure of a parsed block:

export interface ITransportFabricBlock<U, V> {
    hash: string;
    date: Date;
    number: number;
    events: Array<V>;
    transactions: Array<U>;
}

export interface ITransportFabricTransaction<U = any, V = any> {
    hash: string;
    date: Date;
    channel: string;
    requestId: string;
    chaincode: ITransportFabricTransactionChaincode;
    validationCode: FabricTransactionValidationCode;

    request: ITransportFabricRequestPayload<U>;
    response: ITransportFabricResponsePayload<V>;
}

An event carries a reference to the transaction that produced it:

export interface ITransportFabricEvent<T = any> extends ITransportEvent<T> {
    date: Date;
    channel: string;
    requestId: string;
    chaincode: string;
    transactionHash: string;
    transactionValidationCode: FabricTransactionValidationCode;
}

TransportFabricBatch

class TransportFabricBatch<T extends ITransportFabricConnectionSettings> extends TransportFabric<T>

A subclass that delivers state-changing commands in batches. The sending interface does not change — application code still uses send and sendListen, but the response to a state-changing command arrives after the chaincode has executed the batch and it has made it into a block.

TransportFabricBlockParserBatch

class TransportFabricBlockParserBatch extends TransportFabricBlockParser<ITransportFabricTransactionBatch, ITransportFabricEvent, ITransportFabricBlockBatch>

Parses a block containing a batch transaction. In addition to the ordinary parsing, it unpacks the batch: for each hash in the response it loads the original transaction and its block via qscc to reconstruct the full picture.

export interface ITransportFabricTransactionBatch<U = any, V = any> extends ITransportFabricTransaction<U, V> {
    blockReceived: number;
}

The blockReceived field is the number of the block in which the command was accepted, as opposed to the block in which it was executed as part of the batch.

| Method | Purpose | |---|---| | getBatchTransaction(block) | find the batch transaction in a block | | getSucceedBatchTransaction(block) | find a successful batch transaction |

Factories

class TransportFabricFactory extends LoggerWrapper {
    public async get(uid: string): Promise<TransportFabric>;
}

Stores transports by identifier and reuses them: if a transport has already been created but is not connected, get will connect it. Settings are taken from TransportFabricConnectionSettingsFactory, a subclass of the settings factory from @hlf-core/api.

Useful when an application works with several channels or identities at the same time.

Usage Examples

Sending a Command with a Response

let transport = new TransportFabric(logger, settings);
await transport.connect();

let user = await transport.sendListen(new UserGetCommand(uid));

Signing a Command

await transport.sendListen(new UserAddCommand(dto), {
    userId: user.uid,
    signature: { value, algorithm, publicKey, nonce }
});

The command options are extended with the userId and signature fields — the chaincode will verify the signature and the nonce before execution. The current nonce value is requested with the NonceGetCommand command from @hlf-core/transport-common.

Processing Blocks

class MyTransport extends TransportFabric {
    protected async blockEventCallback(block: IFabricBlock): Promise<void> {
        let parser = new TransportFabricBlockParser();
        let item = await parser.parse(block);

        for (let transaction of item.transactions) {
            if (!TransportFabricBlockParser.isTransactionSucceed(transaction)) {
                continue;
            }
            console.log(transaction.request.name, transaction.requestId);
        }

        for (let event of item.events) {
            console.log(event.name, event.requestId, event.transactionValidationCode);
        }
    }
}

Multiple Connections

let factory = new TransportFabricFactory(logger, settingsFactory);

let first = await factory.get('channel-one');
let second = await factory.get('channel-two');

Important Notes

Process termination on disconnect. isExitApplicationOnDisconnect is enabled by default, so disconnect() calls process.exit(1). Note that destroy() internally calls disconnect() — destroying the transport with this setting enabled will terminate the application. If this behavior is undesirable, set the parameter explicitly:

new TransportFabric(logger, { ...settings, isExitApplicationOnDisconnect: false });

Connection retries are disabled by default. reconnectMaxAttempts is zero, which means the very first failed attempt leads to a disconnect — and, with the setting above enabled, to process termination as well. For services that start at the same time as the network, the value is worth setting explicitly.

The first block after connecting is ignored in batch mode. TransportFabricBatch skips the first block that arrives so as not to reprocess what was already processed before reconnection. If that block contained responses to pending commands, they will not be delivered and the commands will time out.

The transport is one-way. eventRequestExecute and commandResponseExecute throw an exception: an event or a response cannot be sent toward the chaincode. The reverse direction is only through blocks.

Command options are modified when sending. getCommandOptions calls clearDefaultOptions on the passed options object, i.e. strips it of default values. Do not reuse the same options object for several commands expecting it to remain unchanged.

Project Structure

src/
├── TransportFabric.ts                         client-side transport
├── ITransportFabricConnectionSettings.ts      connection settings
├── public-api.ts                              package public API
├── block/
│   ├── TransportFabricBlockParser.ts          parsing of blocks, transactions and events
│   ├── ITransportFabricBlock.ts               structure of a parsed block
│   ├── ITransportFabricTransaction.ts         transaction structure
│   ├── ITransportFabricTransactionChaincode.ts chaincode transaction data
│   └── ITransportFabricEvent.ts               event structure
├── batch/
│   ├── TransportFabricBatch.ts                transport with batch delivery
│   └── block/
│       ├── TransportFabricBlockParserBatch.ts parsing of a block with a batch
│       ├── ITransportFabricBlockBatch.ts      structure of a block with a batch
│       └── ITransportFabricTransactionBatch.ts transaction with the receiving block number
└── factory/
    ├── TransportFabricFactory.ts              transport reuse
    └── TransportFabricConnectionSettingsFactory.ts  settings factory

Related Packages

| Package | Role | |---|---| | @hlf-core/transport-common | shared protocol: request and response envelopes, options with a signature | | @hlf-core/transport-chaincode | chaincode side: receiving commands, verifying signatures | | @hlf-core/api | Fabric client: connection, contracts, queries to qscc |

License

ISC

Author

Renat Gubaev

Links

Support

If you have found a bug or have a suggestion for an improvement, please create an issue in GitHub Issues.