Package Exports & TypeScript - FractaLedger

Package Exports & TypeScript

Learn how to use FractaLedger's package exports system and TypeScript support to build type-safe applications.

Package Exports Overview

FractaLedger now provides a modern package exports system that gives users more precise control over how they import and use the library. This feature is part of Node.js's newer module resolution algorithm and offers several benefits:

  • Granular Access Control: Explicitly define which parts of your package are accessible to users
  • Multiple Entry Points: Allow users to import specific functionality without loading the entire package
  • Dual Format Support: Support both CommonJS and ES Modules in the same package
  • TypeScript Integration: Provide type definitions for better IDE support and type checking

With package exports, users can import only the specific functionality they need, which can lead to smaller bundle sizes and better performance in their applications.

CommonJS (Node.js)

// Import the entire package
const fractaledger = require('fractaledger');

// Import specific modules
const { BlockchainConnector } = require('fractaledger/blockchain');
const { initializeWalletManager } = require('fractaledger/wallet');
const { SPVTransceiver } = require('fractaledger/transceivers');
const { startApiServer } = require('fractaledger/api');

ES Modules (Modern JavaScript)

// Import the entire package
import fractaledger from 'fractaledger';

// Import specific modules
import { BlockchainConnector } from 'fractaledger/blockchain';
import { initializeWalletManager } from 'fractaledger/wallet';
import { SPVTransceiver } from 'fractaledger/transceivers';
import { startApiServer } from 'fractaledger/api';

Package Exports Implementation

The package exports feature in FractaLedger is implemented through the exports field in package.json. This field defines the entry points for the package and controls how users can import different parts of the library.

Package Exports Configuration

"exports": {
  ".": {
    "import": "./dist/esm/src/index.js",
    "require": "./dist/src/index.js",
    "types": "./dist/src/index.d.ts"
  },
  "./blockchain": {
    "import": "./dist/esm/src/blockchain/index.js",
    "require": "./dist/src/blockchain/index.js",
    "types": "./dist/src/blockchain/types.d.ts"
  },
  "./wallet": {
    "import": "./dist/esm/src/wallet/walletManager.js",
    "require": "./dist/src/wallet/walletManager.js",
    "types": "./dist/src/wallet/types.d.ts"
  },
  "./transceivers": {
    "import": "./dist/esm/transceivers/index.js",
    "require": "./dist/transceivers/index.js",
    "types": "./dist/transceivers/types.d.ts"
  },
  "./api": {
    "import": "./dist/esm/src/api/index.js",
    "require": "./dist/src/api/index.js",
    "types": "./dist/src/api/types.d.ts"
  },
  "./bin": {
    "init": "./dist/bin/init.js",
    "generate-transceiver": "./dist/bin/generate-transceiver.js",
    "generate-config": "./dist/bin/generate-config.js",
    "generate-api-extension": "./dist/bin/generate-api-extension.js"
  }
}

Each entry in the exports field defines a subpath that users can import. For example, "./blockchain" allows users to import from 'fractaledger/blockchain'. Each subpath can have different entry points for different module formats:

  • import: The entry point for ES Modules (using import syntax)
  • require: The entry point for CommonJS (using require() syntax)
  • types: The TypeScript definition file for this subpath

Module Index Files

To support the package exports structure, we've created index files for each module that re-export the public API:

src/blockchain/index.js

/**
 * Blockchain Module
 * 
 * This module exports all blockchain-related functionality, including:
 * - BlockchainConnector: For interacting with UTXO-based blockchains
 * - TransactionBuilder: For creating and signing transactions
 * - TransceiverManager: For managing transaction broadcasting and wallet monitoring
 * - UTXOTransceiver: Interface for UTXO-based blockchain transceivers
 * - Connector management functions: For initializing and managing blockchain connectors
 */

const { BlockchainConnector } = require('./blockchainConnector');
const { TransactionBuilder, getNetworkParams } = require('./transactionBuilder');
const { TransceiverManager } = require('./transceiverManager');
const { UTXOTransceiver } = require('./utxoTransceiver');
const connectorManager = require('./connectorManager');

module.exports = {
  BlockchainConnector,
  TransactionBuilder,
  TransceiverManager,
  UTXOTransceiver,
  getNetworkParams,
  ...connectorManager
};

Build Process

The build process has been updated to generate both CommonJS and ES Modules versions of the code, as well as TypeScript definitions:

package.json (build scripts)

"scripts": {
  "build:types": "tsc",
  "build:cjs": "mkdir -p dist && cp -r src transceivers fractaledger-template.json README.md LICENSE dist/ && mkdir -p dist/bin && cp -r bin/* dist/bin/ && chmod +x dist/bin/*.js",
  "build:esm": "rollup -c",
  "build": "npm run build:types && npm run build:cjs && npm run build:esm"
}

This build process generates:

  1. TypeScript definitions using tsc
  2. CommonJS modules by copying the source files
  3. ES Modules using Rollup

Rollup Configuration

Rollup is used to bundle the code into ES Modules format. The configuration is defined in rollup.config.js:

rollup.config.js

import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default [
  // Main package
  {
    input: 'src/index.js',
    output: {
      file: 'dist/esm/src/index.js',
      format: 'esm'
    },
    external: ['fs', 'path', 'crypto', 'events', 'util'],
    plugins: [nodeResolve(), commonjs()]
  },
  // Blockchain module
  {
    input: 'src/blockchain/index.js',
    output: {
      file: 'dist/esm/src/blockchain/index.js',
      format: 'esm'
    },
    external: ['fs', 'path', 'crypto', 'events', 'util'],
    plugins: [nodeResolve(), commonjs()]
  },
  // Additional modules would be configured similarly
];

TypeScript Support

FractaLedger now includes comprehensive TypeScript definitions that provide better IDE support, type checking, and documentation for TypeScript users. These definitions are automatically included with the package and are accessible through the package exports system.

Type Definition Files

The following TypeScript definition files are included in the package:

  • src/types.d.ts: Main module type definitions
  • src/blockchain/types.d.ts: Blockchain module type definitions
  • src/wallet/types.d.ts: Wallet module type definitions
  • src/api/types.d.ts: API module type definitions
  • transceivers/types.d.ts: Transceivers module type definitions

These files define the types for the public API of each module, providing better IDE support and type checking for TypeScript users.

Specialized Transaction Interfaces

One of the key improvements in the TypeScript definitions is the creation of specialized interfaces for different transaction operations:

src/blockchain/types.d.ts

/**
 * Transaction options interface
 */
export interface TransactionOptions {
  opReturn?: string;
  fee?: number;
  feeRate?: number;
  utxos: UTXOInput[];  // Required for sendTransaction
}

/**
 * Transaction creation options interface
 */
export interface CreateTransactionOptions {
  opReturn?: string;
  fee?: number;
  feeRate?: number;
  utxos?: UTXOInput[];
}

/**
 * Send transaction options interface
 */
export interface SendTransactionOptions {
  opReturn?: string;  // Optional OP_RETURN data
  fee: number;        // Required transaction fee
  feeRate: number;    // Required fee rate in satoshis per byte
  utxos: UTXOInput[]; // Required UTXOs to use for the transaction
}

These specialized interfaces make it clear which properties are required for each method, improving the developer experience and reducing errors.

Using TypeScript with FractaLedger

When using FractaLedger with TypeScript, you'll get autocompletion and type checking for all FractaLedger APIs:

Example TypeScript Usage

import { BlockchainConnector } from 'fractaledger/blockchain';
import { 
  WalletConfig, 
  TransactionOptions, 
  CreateTransactionOptions, 
  SendTransactionOptions 
} from 'fractaledger/blockchain';

// Create a blockchain connector with proper type checking
const config: WalletConfig = {
  name: 'btc_wallet_1',
  network: 'mainnet',
  walletAddress: 'bc1q...',
  secretEnvVar: 'BTC_WALLET_1_SECRET',
  transceiver: {
    method: 'callback',
    callbackModule: './transceivers/utxo-transceiver.js'
  }
};

const connector = new BlockchainConnector('bitcoin', config);

// Create a transaction with proper type checking
const createOptions: CreateTransactionOptions = {
  opReturn: 'Hello, world!',
  feeRate: 2
};

const transaction = await connector.createTransaction(inputs, outputs, createOptions);

// Send a transaction with proper type checking
const sendOptions: SendTransactionOptions = {
  fee: 10000,
  feeRate: 2,
  utxos: inputs,
  opReturn: 'Hello, world!'
};

const result = await connector.sendTransaction('bc1q...', 0.1, sendOptions);

Working with Optional Properties

In TypeScript, when a property is marked with a question mark (like opReturn?: string), it means:

  1. The property is optional and can be omitted when creating an object of that type.
  2. If included, the property must be of the specified type (in this case, a string).
  3. The type of the property is actually a union type: string | undefined. This means the property can either be a string or undefined.

When working with optional properties in TypeScript, you may encounter type compatibility issues when passing objects to methods that expect specific property types. In these cases, you can use type assertions to tell TypeScript that you know what you're doing:

Using Type Assertions

const sendOptions = {
  fee: 10000,
  feeRate: 2,
  utxos: inputs,
  opReturn: 'Hello, world!'
};

// Use type assertion when TypeScript has trouble with complex interfaces
const result = await connector.sendTransaction('bc1q...', 0.1, sendOptions as any);

This approach allows us to bypass TypeScript's type checking when we encounter complex type compatibility issues. While it's generally better to use proper typing, sometimes TypeScript's type system can be overly strict or have difficulty with certain patterns, especially when dealing with optional properties and complex interfaces.

Package Exports Examples

This section provides practical examples of how to use FractaLedger's package exports in different scenarios. These examples demonstrate the flexibility and power of the new package exports system.

Basic Usage Examples

Importing the Entire Package

// CommonJS
const fractaledger = require('fractaledger');

// ES Modules
import fractaledger from 'fractaledger';

// Use the package
const connector = new fractaledger.BlockchainConnector('bitcoin', config);

Importing Specific Modules

// CommonJS
const { BlockchainConnector } = require('fractaledger/blockchain');
const { initializeWalletManager } = require('fractaledger/wallet');

// ES Modules
import { BlockchainConnector } from 'fractaledger/blockchain';
import { initializeWalletManager } from 'fractaledger/wallet';

// Use the imported modules
const connector = new BlockchainConnector('bitcoin', config);
const walletManager = initializeWalletManager(config);

Advanced Usage Examples

Working with Blockchain Connectors

// Import blockchain module
import { 
  BlockchainConnector, 
  initializeBlockchainConnector,
  getConnectorByName
} from 'fractaledger/blockchain';

// Import types for TypeScript users
import type { 
  WalletConfig, 
  TransactionOptions 
} from 'fractaledger/blockchain';

// Initialize a connector
const config = {
  name: 'btc_wallet_1',
  network: 'testnet',
  walletAddress: 'bc1q...',
  secretEnvVar: 'BTC_WALLET_1_SECRET',
  transceiver: {
    method: 'callback',
    callbackModule: './transceivers/utxo-transceiver.js'
  }
};

// Method 1: Direct instantiation
const connector1 = new BlockchainConnector('bitcoin', config);

// Method 2: Using the initialization function
const connector2 = await initializeBlockchainConnector('bitcoin', config);

// Method 3: Get an existing connector by name
const connector3 = getConnectorByName('btc_wallet_1');

Working with Transceivers

// Import transceivers module
import { 
  UTXOTransceiver,
  SPVTransceiver
} from 'fractaledger/transceivers';

// Import types for TypeScript users
import type { TransceiverConfig } from 'fractaledger/blockchain';

// Create a custom transceiver
class MyCustomTransceiver extends UTXOTransceiver {
  constructor(config) {
    super(config);
    // Custom initialization
  }

  async broadcastTransaction(txHex, metadata) {
    // Custom implementation
    console.log('Broadcasting transaction:', txHex);
    return 'tx_hash_123';
  }

  // Override other methods as needed
}

// Use the custom transceiver
const transceiverConfig = {
  method: 'callback',
  apiUrl: 'https://api.example.com',
  monitoringInterval: 60000
};

const transceiver = new MyCustomTransceiver(transceiverConfig);

Working with Wallet Management

// Import wallet module
import { 
  initializeWalletManager,
  createInternalWallet,
  getInternalWalletBalance
} from 'fractaledger/wallet';

// Initialize wallet manager
const walletManager = initializeWalletManager(config);

// Create an internal wallet
const internalWallet = await createInternalWallet({
  name: 'user_wallet_1',
  primaryWalletName: 'btc_wallet_1',
  description: 'User wallet for John Doe'
});

// Get internal wallet balance
const balance = await getInternalWalletBalance('user_wallet_1');

console.log(`Wallet ${internalWallet.name} has balance: ${balance}`);

TypeScript Examples

Creating and Sending Transactions

import { BlockchainConnector } from 'fractaledger/blockchain';
import type { 
  UTXOInput, 
  UTXOOutput, 
  CreateTransactionOptions,
  SendTransactionOptions,
  TransactionResult
} from 'fractaledger/blockchain';

// Get a connector
const connector = getConnectorByName('btc_wallet_1');

// Define inputs and outputs
const inputs: UTXOInput[] = [
  {
    txid: '0x1234567890abcdef',
    vout: 0,
    value: 100000000,
    height: 700000,
    confirmations: 6
  }
];

const outputs: UTXOOutput[] = [
  {
    address: 'bc1q...',
    value: 50000000
  },
  {
    address: 'bc1q...',
    value: 49990000
  }
];

// Create a transaction
const createOptions: CreateTransactionOptions = {
  opReturn: 'Hello, world!',
  feeRate: 2
};

const transaction: TransactionResult = await connector.createTransaction(
  inputs, 
  outputs, 
  createOptions
);

// Send a transaction
const sendOptions: SendTransactionOptions = {
  fee: 10000,
  feeRate: 2,
  utxos: inputs,
  opReturn: 'Hello, world!'
};

const result = await connector.sendTransaction(
  'bc1q...', 
  0.1, 
  sendOptions
);

Working with Events

import { BlockchainConnector } from 'fractaledger/blockchain';
import type { TransactionEvent } from 'fractaledger/blockchain';

// Get a connector
const connector = getConnectorByName('btc_wallet_1');

// Listen for transaction events
connector.on('transaction', (event: TransactionEvent) => {
  console.log(`New transaction: ${event.txid}`);
  console.log(`Amount: ${event.amount}`);
  console.log(`Confirmations: ${event.confirmations}`);
  
  // Process the transaction
  if (event.confirmations >= 6) {
    console.log('Transaction is confirmed');
  }
});

// Start monitoring a wallet address
await connector.monitorWalletAddress('bc1q...', (event: string, data: any) => {
  console.log(`Event: ${event}`, data);
});

Future Improvements

While the current implementation of package exports and TypeScript support provides a solid foundation, there are several areas where we plan to make further improvements in future releases.

Convert More Files to TypeScript

Currently, we've added TypeScript definition files (.d.ts) to provide type information for our JavaScript code. In the future, we plan to gradually convert more JavaScript files to TypeScript (.ts) to improve type safety and documentation.

Converting to TypeScript will provide several benefits:

  • Better type checking during development
  • Improved IDE support with inline type information
  • More accurate documentation through TypeScript's rich type system
  • Potential to catch bugs earlier in the development process

This conversion will be done incrementally, starting with core modules and gradually expanding to the entire codebase.

Add More TypeScript Tests

We've added initial TypeScript tests to verify the type definitions for the blockchain module. In the future, we plan to add more TypeScript tests to verify the type definitions for all modules, ensuring that our TypeScript definitions accurately reflect the actual behavior of the code.

These tests will help us:

  • Verify that our TypeScript definitions are correct
  • Catch type errors early in the development process
  • Ensure that our TypeScript definitions remain in sync with the code as it evolves
  • Provide examples of how to use the TypeScript definitions

Improve ES Modules Support

While we've added support for ES Modules alongside CommonJS, there's still room for improvement in this area. In the future, we plan to:

  • Convert more code to use ES Modules syntax natively
  • Improve the build process to better handle ES Modules
  • Add more tests for ES Modules usage
  • Provide better documentation for ES Modules users

These improvements will make it easier for users to use FractaLedger in modern JavaScript environments that support ES Modules.

Enhance Package Exports Configuration

We plan to enhance the package exports configuration to provide more granular control over what parts of the package are accessible to users. This may include:

  • Adding more subpaths for specific functionality
  • Providing conditional exports for different environments (browser, Node.js, etc.)
  • Adding support for importing specific components directly
  • Improving the organization of the exports to make it more intuitive for users

Improve Documentation

We'll continue to improve the documentation for package exports and TypeScript support, including:

  • More examples of how to use the package exports in different scenarios
  • Better explanations of the TypeScript type system and how it applies to FractaLedger
  • More detailed API documentation with TypeScript type information
  • Guides for migrating from older versions of FractaLedger to the new package exports system

Feedback and Contributions

We welcome feedback and contributions from the community to help improve the package exports and TypeScript support in FractaLedger. If you have suggestions, bug reports, or would like to contribute code, please:

  • Open an issue on our GitHub repository
  • Submit a pull request with your proposed changes
  • Join our community discussions to share your ideas

Your input is valuable in helping us make FractaLedger more accessible, type-safe, and developer-friendly.