Skip to content

TypeScript SDK

The public package is @leani/sdk, for Node 20+ and Bun 1.2+. In a source checkout, run bun install --cwd packages/sdk and bun run --cwd packages/sdk build. The query/follow guide includes an executable client, a matching node config, and pagination/reset handling.

For a node started with leani init blocks and leani serve:

import { createLeaniClient } from "@leani/sdk";
const leani = createLeaniClient({ baseUrl: "http://127.0.0.1:18080" });
const stop = new AbortController();
for await (const change of leani.blocks.subscribe({ signal: stop.signal })) {
if (change.operation === "apply" && change.data) {
console.log(change.data.blockNumber);
}
}

Pass an AbortSignal to cancel an idle subscription; breaking out of the loop also cancels its response body. Transport disconnects reconnect from the last yielded cursor. Protocol errors and expired history require application action. An incomplete final event is discarded before reconnecting. Retryable HTTP errors, including rate limiting, use the configured reconnect backoff. This print-only example does not maintain state; stateful consumers must apply undo mutations too.

Typed subscriptions accept processor to select a configured instance, for example leani.blobs.subscribe({ processor: "blobs-production" }). The blobs apply payload is { block, transactions }. Narrow operation before reading processor data: finalized carries { throughBlock } instead. Discover canonical query paths with capabilities() and pass queryBasePaths when aliases are ambiguous.

queryAndFollow() returns the first page of an immutable snapshot and its stream boundary. Follow every nextCursor with queryEntities(), release the snapshot in finally, then subscribe from boundaryCursor. SSE resets throw ResetRequiredError; HTTP expiry throws LeaniError with code: "cursor_expired" or "query_snapshot_expired". Rebuild the seed for either case.

queryEntities() also works when delivery is disabled; its boundaryCursor and recovery.follow are then null. queryAndFollow() requires delivery and returns a FollowSnapshotPage with a non-null boundary, or HTTP 409 delivery_disabled. The stream and change endpoints return the same error when delivery is disabled.

Durable live/history sessions live in @leani/sdk/backfill. Construct them with createBackfillSubscriptionClient({ baseUrl, token, fetch, timeoutMs }); both entry points use the same LeaniError class and HTTP error fields (status, code, retryable, requestId). The deadline applies to JSON requests, not to established streams. Always close delivery sessions in finally. Commit idempotent destination writes before acknowledging a batch. See durable delivery for lease and reset semantics.

The inventory below is generated from both public entry points and shared error types. The backfill.* members belong to the backfill client.

baseUrl

MemberContract
baseUrlreadonly baseUrl: URL

request

MemberContract
requestrequest<T>( path: string, params?: Record<string, QueryValue>, options?: { signal?: AbortSignal }, ): Promise<T>

health

MemberContract
health.livelive(options?: { signal?: AbortSignal }): Promise<Health>
health.readyready(options?: { signal?: AbortSignal }): Promise<Health>

status

MemberContract
statusstatus(options?: { signal?: AbortSignal }): Promise<NodeStatus>

capabilities

MemberContract
capabilitiescapabilities(options?: { signal?: AbortSignal }): Promise<Capabilities>

processors

MemberContract
processors.listlist(options?: { signal?: AbortSignal }): Promise<ProcessorSummary[]>
processors.statusstatus( id: string, options?: { signal?: AbortSignal }, ): Promise<ProcessorCoverage>
processors.changeschanges<T = unknown>( id: string, options?: ChangesOptions, ): Promise<Page<ChangeEnvelope<T>>>
processors.changeHeadchangeHead( id: string, options?: { signal?: AbortSignal }, ): Promise<ChangeHead>
processors.resetLiveLaneresetLiveLane( id: string, options?: { signal?: AbortSignal }, ): Promise<LiveLaneReset>
processors.listCollectionslistCollections( id: string, options?: { signal?: AbortSignal }, ): Promise<{ processor: ProcessorSummary; outputMode: "none" | "latest" | "window" | "full"; retained: boolean; data: string[]; }>
processors.queryEntitiesqueryEntities<T = unknown>( id: string, collection: string, options?: OutputQueryOptions, ): Promise<GenericSnapshotPage<T>>
processors.queryAndFollowqueryAndFollow<T = unknown>( id: string, collection: string, options?: Omit<OutputQueryOptions, "cursor">, ): Promise<FollowSnapshotPage<T>>
processors.getEntitygetEntity<T = unknown>( id: string, collection: string, key: Hex, options?: { signal?: AbortSignal }, ): Promise<GenericOutputEntity<T>>
processors.releaseSnapshotreleaseSnapshot( id: string, snapshotId: string, options?: { signal?: AbortSignal }, ): Promise<void>
processors.consumers.listlist( id: string, options?: { signal?: AbortSignal }, ): Promise<DurableConsumer[]>
processors.consumers.createcreate( id: string, options: CreateConsumerOptions, ): Promise<DurableConsumer>
processors.consumers.inspectinspect( id: string, consumer: string, options?: { signal?: AbortSignal }, ): Promise<DurableConsumer>
processors.consumers.revokerevoke( id: string, consumer: string, options?: { signal?: AbortSignal }, ): Promise<DurableConsumer>
processors.consumers.renewrenew( id: string, consumer: string, credential: string, options?: { signal?: AbortSignal }, ): Promise<DurableConsumer>
processors.consumers.changeschanges<T = unknown>( id: string, consumer: string, credential: string, options?: { limit?: number; signal?: AbortSignal }, ): Promise<Page<ChangeEnvelope<T>>>
processors.consumers.acknowledgeacknowledge( id: string, consumer: string, credential: string, cursor: string, options?: { signal?: AbortSignal }, ): Promise<DurableConsumer>
processors.subscribesubscribe<T = unknown>( id: string, options?: SubscribeOptions, ): AsyncGenerator<ChangeEnvelope<T>>

blobs

MemberContract
blobs.getBlockgetBlock( number: number, options?: { signal?: AbortSignal }, ): Promise<BlobsBlock>
blobs.listBlockslistBlocks(options: ListBlocksOptions): Promise<Page<BlobsBlock>>
blobs.listSnapshotlistSnapshot( options: ListBlocksOptions, ): Promise<Page<BlobsSnapshotEntry>>
blobs.getTransactiongetTransaction( hash: Hex, options?: { signal?: AbortSignal }, ): Promise<BlobTransaction>
blobs.listTransactionslistTransactions( options: ListTransactionsOptions, ): Promise<Page<BlobTransaction>>
blobs.getSchedulegetSchedule(options?: { atBlock?: number; signal?: AbortSignal; }): Promise<BlobSchedule>
blobs.subscribesubscribe( options?: ProcessorSubscribeOptions, ): AsyncGenerator<ChangeEnvelope<BlobsSnapshotEntry>>

erc20

MemberContract
erc20.getBalancegetBalance( token: Hex, address: Hex, options?: { signal?: AbortSignal }, ): Promise<Erc20Balance>
erc20.subscribesubscribe( options?: ProcessorSubscribeOptions, ): AsyncGenerator<ChangeEnvelope<Erc20Balance>>

uniswap

MemberContract
uniswap.getPoolgetPool( address: Hex, options?: { signal?: AbortSignal }, ): Promise<UniswapPoolPrice>
uniswap.getLatestObservationgetLatestObservation( address: Hex, options?: { processor?: string; signal?: AbortSignal }, ): Promise<LatestUniswapObservation>
uniswap.listConfiguredPoolslistConfiguredPools(options?: { processor?: string; signal?: AbortSignal; }): Promise<ConfiguredUniswapPools>
uniswap.subscribesubscribe( options?: ProcessorSubscribeOptions, ): AsyncGenerator<ChangeEnvelope<UniswapPoolPrice>>

blocks

MemberContract
blocks.getLatestgetLatest(options?: { processor?: string; signal?: AbortSignal; }): Promise<LatestBlockSummary>
blocks.getByNumbergetByNumber( number: number, options?: { processor?: string; signal?: AbortSignal }, ): Promise<LatestBlockSummary>
blocks.subscribesubscribe( options?: ProcessorSubscribeOptions, ): AsyncGenerator<ChangeEnvelope<BlockSummary>>

backfill

MemberContract
backfill.baseUrlreadonly baseUrl: URL
backfill.createcreate(request: CreateBackfillSubscription): Promise<BackfillStatus>
backfill.listlist(options?: { signal?: AbortSignal }): Promise<BackfillStatus[]>
backfill.inspectinspect( subscription: string, options?: { signal?: AbortSignal }, ): Promise<BackfillStatus>
backfill.cancelcancel( subscription: string, options?: { signal?: AbortSignal }, ): Promise<BackfillStatus>
backfill.deletedelete( subscription: string, options?: { signal?: AbortSignal }, ): Promise<HistoricalWorkDeletion>
backfill.changeschanges<T = unknown>( subscription: string, consumer: string, options?: { credential?: string; limit?: number; signal?: AbortSignal; }, ): Promise<{ data: ChangeEnvelope<T>[]; nextCursor: string | null; coverage: unknown; }>
backfill.streamstream<T = unknown>( subscription: string, consumer: string, options?: { credential?: string; batching?: BackfillBatchingRequest; signal?: AbortSignal; }, ): Promise<BackfillDeliverySession<T>>
backfill.streamLivestreamLive<T = unknown>( processor: string, consumer: string, options?: { credential?: string; signal?: AbortSignal }, ): Promise<LiveDeliverySession<T>>
backfill.subscribesubscribe<T = unknown>( request: MultiLaneSubscriptionRequest, ): Promise<MultiLaneDelivery<T>>

Exported types and helpers

Hex@leani/sdk
export type Hex = `0x${string}`;
Finality@leani/sdk
export type Finality = "preview" | "included" | "finalized";
ChangeOperation@leani/sdk
export type ChangeOperation =
  | "apply"
  | "undo"
  | "finalized"
  | "reset_required";
LeaniClientOptions@leani/sdk
export interface LeaniClientOptions {
  baseUrl: string | URL;
  token?: string;
  timeoutMs?: number;
  fetch?: FetchLike;
  /**
   * Override typed query-extension mounts with canonical `basePath` values
   * returned by `capabilities()` when an ergonomic alias is unavailable.
   */
  queryBasePaths?: {
    blobs?: string;
    erc20?: string;
    uniswap?: string;
  };
  reconnect?: {
    initialDelayMs?: number;
    maxDelayMs?: number;
    jitter?: number;
  };
}
FetchLike@leani/sdk
export type FetchLike = (
  input: RequestInfo | URL,
  init?: RequestInit,
) => Promise<Response>;
ProcessorSummary@leani/sdk
export interface ProcessorSummary {
  id: string;
  instance: string;
  version: string;
  codeHash: Hex;
  configHash: Hex;
  genericApi: "processor-v1";
  changeSchema: string;
  queryExtensions: QueryExtensionSummary[];
  subscriptions: boolean;
  artifactRetention: "none" | "window" | "full";
  deliveryOrdering: "canonical" | "block_versioned_idempotent";
}
QueryExtensionSummary@leani/sdk
export interface QueryExtensionSummary {
  id: string;
  basePath: string;
  aliasPath: string | null;
}
QueryExtensionCapability@leani/sdk
export interface QueryExtensionCapability extends QueryExtensionSummary {
  processor: string;
}
Capabilities@leani/sdk
export interface Capabilities {
  chainId: number;
  ethereumRpc: Record<string, unknown>;
  processors: ProcessorSummary[];
  queryExtensions: QueryExtensionCapability[];
}
NodeStatus@leani/sdk
export interface NodeStatus {
  apiVersion: "1";
  project: string;
  version: string;
  chainId: number;
  uptimeSeconds: number;
  databaseBytes: number;
  processor: ProcessorSummary;
  processors: ProcessorSummary[];
  readiness: Readiness;
}
Readiness@leani/sdk
export interface Readiness {
  liveRequired: boolean;
  liveReady: boolean;
  finalityRequired: boolean;
  finalityReady: boolean;
  ready: boolean;
}
Health@leani/sdk
export interface Health {
  status: "live" | "ready" | "not_ready" | "failed";
  uptimeSeconds: number;
  database: "available" | "unavailable";
  readiness: Readiness;
  reasons: string[];
}
CoverageInterval@leani/sdk
export interface CoverageInterval {
  fromBlock: number;
  toBlock: number;
  finality: Finality;
}
ProcessorCoverage@leani/sdk
export interface ProcessorCoverage {
  chainId: number;
  chainFinalizedHead: {
    number: number;
    hash: string;
  } | null;
  requested?: {
    fromBlock: number;
    toBlock: number;
  };
  available: CoverageInterval[];
  configuredStartBlock: number;
  processedThrough: number | null;
  finalizedThrough: number | null;
  complete: boolean;
  state:
    | "starting"
    | "backfilling"
    | "catching_up"
    | "live"
    | "degraded"
    | "failed";
}
Page@leani/sdk
export interface Page<T> {
  data: T[];
  nextCursor: string | null;
  coverage: ProcessorCoverage;
}
BlobsBlock@leani/sdk
export interface BlobsBlock {
  network: string;
  blockNumber: number;
  blockHash: Hex;
  parentHash: Hex;
  timestamp: number;
  size: string;
  blobCount: number;
  blobGasUsed: string;
  excessBlobGas: string;
  blobBaseFee: string;
  executionBaseFee: string;
  gasUsed: string;
  gasLimit: string;
  executionEthBurnedWei: string;
  blobEthBurnedWei: string;
  reserveFeeWei: string | null;
  transactionCount: number;
  targetBlobsPerBlock: number;
  maxBlobsPerBlock: number;
  transformVersion: number;
  finality: Finality;
}
BlobTransaction@leani/sdk
export interface BlobTransaction {
  network: string;
  blockNumber: number;
  blockHash: Hex;
  txHash: Hex;
  transactionIndex: number;
  senderAddress: Hex;
  blobVersionedHashes: Hex[];
  blobCount: number;
  totalBurnedWei: string;
  executionBurnedWei: string;
  blobBurnedWei: string;
}
BlobsSnapshotEntry@leani/sdk
export interface BlobsSnapshotEntry {
  block: BlobsBlock;
  transactions: BlobTransaction[];
}
ChangeHead@leani/sdk
export interface ChangeHead {
  earliestSequence: string | null;
  latestSequence: string | null;
  cursor: string | null;
}
GenericOutputEntity@leani/sdk
export interface GenericOutputEntity<T = unknown> {
  ordinal: string;
  key: Hex;
  schema: string;
  data: T;
  blockNumber: number;
  blockTimestamp: number;
  finality: Finality;
}
OutputBounds@leani/sdk
export interface OutputBounds {
  earliestBlock: number;
  latestBlock: number;
  earliestTimestamp: number;
  latestTimestamp: number;
}
GenericSnapshotPage@leani/sdk
export interface GenericSnapshotPage<T = unknown> {
  data: GenericOutputEntity<T>[];
  nextCursor: string | null;
  snapshotId: string;
  boundaryCursor: string | null;
  rowCount: string;
  valueBytes: string;
  expiresAtUnixMs: string;
  retainedBounds: OutputBounds | null;
  coverage: ProcessorCoverage;
  recovery: {
    follow: string | null;
    outsideRetention: "create_processor_instance_or_source_scan";
  };
}
FollowSnapshotPage@leani/sdk
export interface FollowSnapshotPage<T = unknown> extends GenericSnapshotPage<T> {
  boundaryCursor: string;
  recovery: GenericSnapshotPage<T>["recovery"] & { follow: string };
}
OutputQueryOptions@leani/sdk
export interface OutputQueryOptions {
  fromBlock?: number;
  toBlock?: number;
  fromTimestamp?: number;
  toTimestamp?: number;
  limit?: number;
  cursor?: string;
  signal?: AbortSignal;
}
ConsumerRole@leani/sdk
export type ConsumerRole = "required" | "best_effort";
ConsumerState@leani/sdk
export type ConsumerState = "active" | "reset_required" | "revoked";
ConsumerStart@leani/sdk
export type ConsumerStart =
  | { position: "earliest_retained" }
  | { position: "current_head" }
  | { position: "cursor"; cursor: string };
CreateConsumerOptions@leani/sdk
export interface CreateConsumerOptions {
  id: string;
  role: ConsumerRole;
  start: ConsumerStart;
  leaseTtlSeconds: number;
  credential: string;
  signal?: AbortSignal;
}
DurableConsumer@leani/sdk
export interface DurableConsumer {
  id: string;
  processorInstance: string;
  role: ConsumerRole;
  state: ConsumerState;
  acknowledgedSequence: string;
  deliveredSequence: string;
  acknowledgedCursor: string;
  deliveredCursor: string;
  leaseTtlMs: string;
  leaseExpiresAtUnixMs: string;
  leaseActive: boolean;
  lagChanges: string;
  lagBlocks: string;
  lagBytes: string;
  lagAgeMs: string;
  createdAtUnixMs: string;
  updatedAtUnixMs: string;
}
BlobSchedule@leani/sdk
export interface BlobSchedule {
  name: string;
  activationBlock: number;
  activationTimestamp: number;
  forkId: Hex;
  targetBlobsPerBlock: number;
  maxBlobsPerBlock: number;
  baseFeeUpdateFraction: string;
  eip7918: boolean;
  source: "chain_spec" | "eth_config" | "configured";
}
Erc20Balance@leani/sdk
export interface Erc20Balance {
  token: Hex;
  address: Hex;
  balance: string;
  asOfBlock: number;
  blockHash: Hex;
  finality: Finality;
  coverageFrom: number;
  complete: boolean;
  method: "erc20_transfer_ledger";
}
EvmEvent@leani/sdk
export interface EvmEvent {
  schemaVersion: number;
  contract: Hex;
  event: string;
  signature: string;
  blockNumber: number;
  blockHash: Hex;
  blockTimestamp: number;
  bucketStartTimestamp?: number;
  transactionHash: Hex;
  transactionIndex: number;
  logIndex: number;
  finality: Finality;
  values: Record<string, string>;
}
TransactionStats@leani/sdk
export interface TransactionStats {
  from: Hex;
  to: Hex;
  count: number;
  totalValueWei: string;
  asOfBlock: number;
  blockHash: Hex;
  finality: Finality;
  coverageFrom: number;
  complete: boolean;
}
UniswapPoolPrice@leani/sdk
export interface UniswapPoolPrice {
  pool: Hex;
  kind: "v2" | "v3";
  reserve0: string | null;
  reserve1: string | null;
  /** Signed token0 pool delta for a V3 swap, in raw token units. */
  amount0: string | null;
  /** Signed token1 pool delta for a V3 swap, in raw token units. */
  amount1: string | null;
  sqrtPriceX96: string | null;
  blockNumber: number;
  blockHash: Hex;
  logIndex: number;
  finality: Finality;
}
LatestUniswapObservation@leani/sdk
export interface LatestUniswapObservation {
  data: UniswapPoolPrice;
  timestamp: number;
}
ConfiguredUniswapPool@leani/sdk
export interface ConfiguredUniswapPool {
  address: Hex;
  kind: "v2" | "v3";
}
ConfiguredUniswapPools@leani/sdk
export interface ConfiguredUniswapPools {
  data: ConfiguredUniswapPool[];
}
BlockSummary@leani/sdk
export interface BlockSummary {
  chainId: number;
  blockNumber: number;
  blockHash: Hex;
  parentHash: Hex;
  timestamp: number;
  gasLimit: number | null;
  gasUsed: number | null;
  baseFeePerGas: string | null;
  blobGasUsed: number | null;
  excessBlobGas: number | null;
  transactionCount: number | null;
  sizeBytes: number | null;
  finality: Finality;
}
LatestBlockSummary@leani/sdk
export interface LatestBlockSummary {
  data: BlockSummary;
}
ChangeMetadata@leani/sdk
export interface ChangeMetadata {
  apiVersion: "1";
  sequence: string;
  cursor: string;
  originKind:
    | "live"
    | "live_recovery"
    | "historical_backfill"
    | "recompute";
  originId: string;
  publicationRevision: string;
  chainId: number;
  block: {
    number: number;
    hash: Hex;
    parentHash: Hex;
    timestamp: number;
  } | null;
  finality: Finality;
  kind: string;
  schema: string;
  key: string | null;
  /** Present only on `reset_required` events, as the current coverage hint. */
  coverage?: ProcessorCoverage;
  /** RFC 3339 UTC emission time with millisecond precision. */
  emittedAt: string;
}
ChangeEnvelope@leani/sdk
export type ChangeEnvelope<T = unknown> = ChangeMetadata & (
  | { operation: "apply" | "undo"; data: T | null }
  | { operation: "finalized"; data: { throughBlock: number } }
  | { operation: "reset_required"; data: {
      earliestAvailableSequence: string;
      latestAvailableSequence: string;
      action: "query_snapshot_then_resume";
    } }
);
LiveLaneReset@leani/sdk
export interface LiveLaneReset {
  processor: string;
  state: "paused";
  reason: "operator_reset_pending_replay";
  firstUnappliedBlock: number;
  firstUnappliedHash: Hex;
  requiredDeliveryBytes: string;
}
StreamHello@leani/sdk
export interface StreamHello {
  apiVersion: "1";
  chainId: number;
  processor: ProcessorSummary;
  coverage: ProcessorCoverage;
}
ListBlocksOptions@leani/sdk
export interface ListBlocksOptions {
  fromBlock: number;
  toBlock: number;
  limit?: number;
  cursor?: string;
  allowPartial?: boolean;
  signal?: AbortSignal;
}
ListTransactionsOptions@leani/sdk
export interface ListTransactionsOptions {
  blockNumber: number;
  limit?: number;
  cursor?: string;
  signal?: AbortSignal;
}
ChangesOptions@leani/sdk
export interface ChangesOptions {
  after?: string;
  limit?: number;
  signal?: AbortSignal;
}
SubscribeOptions@leani/sdk
export interface SubscribeOptions {
  after?: string;
  signal?: AbortSignal;
  /** Receives the connection handshake frame; called once per (re)connect. */
  onHello?: (hello: StreamHello) => void;
}
ProcessorSubscribeOptions@leani/sdk
export interface ProcessorSubscribeOptions extends SubscribeOptions {
  processor?: string;
}
LeaniClient@leani/sdk
export interface LeaniClient {
  readonly baseUrl: URL;
  request<T>(
    path: string,
    params?: Record<string, QueryValue>,
    options?: { signal?: AbortSignal },
  ): Promise<T>;
  health: {
    live(options?: { signal?: AbortSignal }): Promise<Health>;
    ready(options?: { signal?: AbortSignal }): Promise<Health>;
  };
  status(options?: { signal?: AbortSignal }): Promise<NodeStatus>;
  capabilities(options?: { signal?: AbortSignal }): Promise<Capabilities>;
  processors: {
    list(options?: { signal?: AbortSignal }): Promise<ProcessorSummary[]>;
    status(
      id: string,
      options?: { signal?: AbortSignal },
    ): Promise<ProcessorCoverage>;
    changes<T = unknown>(
      id: string,
      options?: ChangesOptions,
    ): Promise<Page<ChangeEnvelope<T>>>;
    changeHead(
      id: string,
      options?: { signal?: AbortSignal },
    ): Promise<ChangeHead>;
    resetLiveLane(
      id: string,
      options?: { signal?: AbortSignal },
    ): Promise<LiveLaneReset>;
    listCollections(
      id: string,
      options?: { signal?: AbortSignal },
    ): Promise<{
      processor: ProcessorSummary;
      outputMode: "none" | "latest" | "window" | "full";
      retained: boolean;
      data: string[];
    }>;
    queryEntities<T = unknown>(
      id: string,
      collection: string,
      options?: OutputQueryOptions,
    ): Promise<GenericSnapshotPage<T>>;
    /** First snapshot page plus an atomic stream boundary. Read all nextCursor pages and releaseSnapshot before following. */
    queryAndFollow<T = unknown>(
      id: string,
      collection: string,
      options?: Omit<OutputQueryOptions, "cursor">,
    ): Promise<FollowSnapshotPage<T>>;
    getEntity<T = unknown>(
      id: string,
      collection: string,
      key: Hex,
      options?: { signal?: AbortSignal },
    ): Promise<GenericOutputEntity<T>>;
    releaseSnapshot(
      id: string,
      snapshotId: string,
      options?: { signal?: AbortSignal },
    ): Promise<void>;
    consumers: {
      list(
        id: string,
        options?: { signal?: AbortSignal },
      ): Promise<DurableConsumer[]>;
      create(
        id: string,
        options: CreateConsumerOptions,
      ): Promise<DurableConsumer>;
      inspect(
        id: string,
        consumer: string,
        options?: { signal?: AbortSignal },
      ): Promise<DurableConsumer>;
      revoke(
        id: string,
        consumer: string,
        options?: { signal?: AbortSignal },
      ): Promise<DurableConsumer>;
      renew(
        id: string,
        consumer: string,
        credential: string,
        options?: { signal?: AbortSignal },
      ): Promise<DurableConsumer>;
      changes<T = unknown>(
        id: string,
        consumer: string,
        credential: string,
        options?: { limit?: number; signal?: AbortSignal },
      ): Promise<Page<ChangeEnvelope<T>>>;
      acknowledge(
        id: string,
        consumer: string,
        credential: string,
        cursor: string,
        options?: { signal?: AbortSignal },
      ): Promise<DurableConsumer>;
    };
    subscribe<T = unknown>(
      id: string,
      options?: SubscribeOptions,
    ): AsyncGenerator<ChangeEnvelope<T>>;
  };
  blobs: {
    getBlock(
      number: number,
      options?: { signal?: AbortSignal },
    ): Promise<BlobsBlock>;
    listBlocks(options: ListBlocksOptions): Promise<Page<BlobsBlock>>;
    listSnapshot(
      options: ListBlocksOptions,
    ): Promise<Page<BlobsSnapshotEntry>>;
    getTransaction(
      hash: Hex,
      options?: { signal?: AbortSignal },
    ): Promise<BlobTransaction>;
    listTransactions(
      options: ListTransactionsOptions,
    ): Promise<Page<BlobTransaction>>;
    getSchedule(options?: {
      atBlock?: number;
      signal?: AbortSignal;
    }): Promise<BlobSchedule>;
    subscribe(
      options?: ProcessorSubscribeOptions,
    ): AsyncGenerator<ChangeEnvelope<BlobsSnapshotEntry>>;
  };
  erc20: {
    getBalance(
      token: Hex,
      address: Hex,
      options?: { signal?: AbortSignal },
    ): Promise<Erc20Balance>;
    subscribe(
      options?: ProcessorSubscribeOptions,
    ): AsyncGenerator<ChangeEnvelope<Erc20Balance>>;
  };
  uniswap: {
    getPool(
      address: Hex,
      options?: { signal?: AbortSignal },
    ): Promise<UniswapPoolPrice>;
    getLatestObservation(
      address: Hex,
      options?: { processor?: string; signal?: AbortSignal },
    ): Promise<LatestUniswapObservation>;
    listConfiguredPools(options?: {
      processor?: string;
      signal?: AbortSignal;
    }): Promise<ConfiguredUniswapPools>;
    subscribe(
      options?: ProcessorSubscribeOptions,
    ): AsyncGenerator<ChangeEnvelope<UniswapPoolPrice>>;
  };
  blocks: {
    getLatest(options?: {
      processor?: string;
      signal?: AbortSignal;
    }): Promise<LatestBlockSummary>;
    getByNumber(
      number: number,
      options?: { processor?: string; signal?: AbortSignal },
    ): Promise<LatestBlockSummary>;
    subscribe(
      options?: ProcessorSubscribeOptions,
    ): AsyncGenerator<ChangeEnvelope<BlockSummary>>;
  };
}
createLeaniClient@leani/sdk
export function createLeaniClient(
  options: LeaniClientOptions,
): LeaniClient;
QueryValue@leani/sdk
export type QueryValue = string | number | boolean | undefined;
parseSse@leani/sdk
export async function* parseSse(
  body: ReadableStream<Uint8Array>,
  signal?: AbortSignal,
): AsyncGenerator<SseMessage>;
compareSequences@leani/sdk
export function compareSequences(left: string, right: string): -1 | 0 | 1;
isResetRequired@leani/sdk
export function isResetRequired(
  change: ChangeEnvelope,
): change is ChangeEnvelope & { operation: "reset_required" };
EntityChangeTarget@leani/sdk
export interface EntityChangeTarget<T> {
  put(key: string, value: T): void | Promise<void>;
  delete(key: string): void | Promise<void>;
}
applyEntityChange@leani/sdk
export async function applyEntityChange<T>(
  target: EntityChangeTarget<T>,
  change: ChangeEnvelope<T>,
): Promise<void>;
commitThenAcknowledge@leani/sdk
export async function commitThenAcknowledge<T>(
  destinationCommit: () => Promise<T>,
  acknowledge: (committed: T) => Promise<unknown>,
): Promise<T>;
CursorStore@leani/sdk
export interface CursorStore {
  get(): string | undefined;
  set(cursor: string): void;
  clear(): void;
}
createInMemoryCursorStore@leani/sdk
export function createInMemoryCursorStore(initial?: string): CursorStore;
BackfillExecutionMode@leani/sdk/backfill
export type BackfillExecutionMode = "fill_missing" | "recompute";
BackfillState@leani/sdk/backfill
export type BackfillState =
  | "waiting_for_consumer"
  | "queued"
  | "running"
  | "backpressured"
  | "storage_backpressured"
  | "draining"
  | "complete_reclaimable"
  | "completed"
  | "failed"
  | "cancelled";
BackfillRange@leani/sdk/backfill
export interface BackfillRange {
  fromBlock: number;
  toBlock: number;
}
RequestedBackfillRange@leani/sdk/backfill
export interface RequestedBackfillRange {
  fromBlock: number;
  toBlock: number | "finalized";
}
BackfillStatus@leani/sdk/backfill
export interface BackfillStatus {
  id: string;
  owner: "subscription";
  processor: string;
  deliveryStreamId?: string;
  publicationRevision?: string;
  fromBlock: number;
  toBlock: number;
  ranges: BackfillRange[];
  requestedBlocks: number;
  processedBlocks: number;
  remainingBlocks: number;
  capturedFinalizedTarget?: number;
  mode: BackfillExecutionMode;
  batching?: EffectiveBackfillBatching;
  state: BackfillState;
  attempts: number;
  updatedAtUnixMs: number;
  report: unknown | null;
  lastError: string | null;
}
HistoricalWorkDeletion@leani/sdk/backfill
export interface HistoricalWorkDeletion {
  id: string;
  owner: "subscription";
  removedJobs: number;
  removedSubscriptionRanges: number;
  removedConsumers: number;
  removedDeliveryRecords: number;
  removedDeliveryStreams: number;
  removedCoverageIntervals: number;
  removedCoverageSegments: number;
  removedExactCoverage: number;
  removedAppliedBlocks: number;
  removedFinalizedUndo: number;
  retainedProcessorOutput: boolean;
  retainedLiveStream: boolean;
}
BackfillBatchingRequest@leani/sdk/backfill
export type BackfillBatchingRequest = Partial<
  Omit<
    EffectiveBackfillBatching,
    "maximumBufferedBatches" | "maximumBufferedBytes"
  >
>;
BackfillBatchingProfile@leani/sdk/backfill
export type BackfillBatchingProfile = "latency" | "balanced" | "throughput";
backfillBatchingProfile@leani/sdk/backfill
export function backfillBatchingProfile(
  profile: BackfillBatchingProfile,
): Required<BackfillBatchingRequest>;
EffectiveBackfillBatching@leani/sdk/backfill
export interface EffectiveBackfillBatching {
  targetEncodedBytes: number;
  maximumEncodedBytes: number;
  maximumEvents: number;
  maximumProcessedBlocks: number;
  maximumDelayMs: number;
  maximumBufferedBatches: number;
  maximumBufferedBytes: number;
  compression: "none" | "gzip";
}
CreateBackfillSubscription@leani/sdk/backfill
export type CreateBackfillSubscription = CreateBackfillSubscriptionBase &
  (
    | {
        fromBlock: number;
        toBlock: number | "finalized";
        ranges?: never;
      }
    | {
        ranges: RequestedBackfillRange[];
        fromBlock?: never;
        toBlock?: never;
      }
  );
BackfillStreamHello@leani/sdk/backfill
export interface BackfillStreamHello {
  type: "hello";
  apiVersion: "1";
  chainId: number;
  processor: ProcessorSummary;
  subscriptionId: string;
  streamId: string;
  streamKind: "backfill";
  publicationRevision: string;
  ranges: BackfillRange[];
  acknowledgedCursor: string;
  storeEpoch: string;
  heartbeatIntervalMs: string;
  sessionExpiresAtUnixMs: string;
  leaseTtlMs: string;
}
BackfillStreamBatch@leani/sdk/backfill
export interface BackfillStreamBatch<T = unknown> {
  type: "batch";
  streamId: string;
  originKind: "historical_backfill" | "recompute";
  originId: string;
  publicationRevision: string;
  fromBlock: number;
  throughBlock: number;
  processedBlockCount: string;
  domainChangeCount: string;
  progressUnitCount: string;
  rawPayloadBytes: string;
  uncompressedEncodedBytes: string;
  transmittedBytes: string;
  buildDelayMs: string;
  firstCursor: string | null;
  lastCursor: string | null;
  acknowledgeableCursor: string;
  changes: ChangeEnvelope<T>[];
}
BackfillStreamCompletion@leani/sdk/backfill
export interface BackfillStreamCompletion {
  type: "backfill_complete";
  subscriptionId: string;
  streamId: string;
  ranges: BackfillRange[];
  throughBlock: number;
  cursor: string;
  mode: BackfillExecutionMode;
  disposition:
    | "published_all"
    | "published_missing_only"
    | "already_covered_noop";
  requestedBlockCount: string;
  coveredBeforeRequestBlockCount: string;
  coveredBeforeRequestRanges: BackfillRange[];
  newlyProcessedBlockCount: string;
  republishedBlockCount: string;
  domainChangeCount: string;
  preexistingCoverageSkipped: boolean;
  repairHint?: "recompute";
}
BackfillStreamHeartbeat@leani/sdk/backfill
export interface BackfillStreamHeartbeat {
  type: "heartbeat";
  emittedAt: string;
}
BackfillStreamReset@leani/sdk/backfill
export interface BackfillStreamReset {
  type: "reset_required";
  code: "reset_required";
  message: string;
  earliestAvailableSequence: string | null;
  latestAvailableSequence: string | null;
}
BackfillStreamFailure@leani/sdk/backfill
export interface BackfillStreamFailure {
  type: "error";
  code: string;
  message: string;
  earliestAvailableSequence: string | null;
  latestAvailableSequence: string | null;
}
BackfillStreamRecord@leani/sdk/backfill
export type BackfillStreamRecord<T = unknown> =
  | BackfillStreamBatch<T>
  | BackfillStreamCompletion
  | BackfillStreamHeartbeat;
LiveStreamHello@leani/sdk/backfill
export interface LiveStreamHello {
  type: "hello";
  apiVersion: "1";
  chainId: number;
  processor: ProcessorSummary;
  streamId: string;
  streamKind: "live";
  acknowledgedCursor: string;
  storeEpoch: string;
  heartbeatIntervalMs: string;
  sessionExpiresAtUnixMs: string;
  leaseTtlMs: string;
}
LiveStreamBatch@leani/sdk/backfill
export interface LiveStreamBatch<T = unknown> {
  type: "batch";
  streamId: string;
  originKind: "live" | "live_recovery";
  originId: string;
  publicationRevision: "0";
  fromBlock: number;
  throughBlock: number;
  processedBlockCount: "1";
  domainChangeCount: string;
  progressUnitCount: "1";
  rawPayloadBytes: string;
  uncompressedEncodedBytes: string;
  transmittedBytes: string;
  buildDelayMs: string;
  firstCursor: string;
  lastCursor: string;
  acknowledgeableCursor: string;
  changes: ChangeEnvelope<T>[];
}
LiveStreamRecord@leani/sdk/backfill
export type LiveStreamRecord<T = unknown> =
  | LiveStreamBatch<T>
  | BackfillStreamHeartbeat;
DeliverySession@leani/sdk/backfill
export interface DeliverySession<THello, TRecord>
  extends AsyncIterable<TRecord> {
  readonly hello: THello;
  acknowledge(
    cursor: string,
    options?: { signal?: AbortSignal },
  ): Promise<DurableConsumer>;
  close(): Promise<void>;
}
BackfillDeliverySession@leani/sdk/backfill
export interface BackfillDeliverySession<T = unknown>
  extends DeliverySession<BackfillStreamHello, BackfillStreamRecord<T>> {
  batches(): AsyncIterable<BackfillStreamBatch<T>>;
  events(): AsyncIterable<ChangeEnvelope<T>>;
}
LiveDeliverySession@leani/sdk/backfill
export type LiveDeliverySession<T = unknown> = DeliverySession<
  LiveStreamHello,
  LiveStreamRecord<T>
>;
MultiLaneSubscriptionRequest@leani/sdk/backfill
export interface MultiLaneSubscriptionRequest {
  processor: string;
  consumer: string;
  credential?: string;
  lanes: {
    live?: boolean;
    history?: Array<{ subscriptionId: string }>;
  };
  view?: "live_priority" | "lane_specific";
  batching?: BackfillBatchingRequest;
  signal?: AbortSignal;
}
UnifiedDeliveryBatch@leani/sdk/backfill
export type UnifiedDeliveryBatch<T = unknown> =
  | {
      laneKind: "live";
      laneId: "live";
      streamId: string;
      ackCursor: string;
      events: ChangeEnvelope<T>[];
      completion?: never;
      record: LiveStreamBatch<T>;
    }
  | {
      laneKind: "history";
      laneId: string;
      streamId: string;
      ackCursor: string;
      events: ChangeEnvelope<T>[];
      completion?: BackfillStreamCompletion;
      record: BackfillStreamBatch<T> | BackfillStreamCompletion;
    };
MultiLaneDelivery@leani/sdk/backfill
export interface MultiLaneDelivery<T = unknown> {
  batches(): AsyncIterable<UnifiedDeliveryBatch<T>>;
  liveBatches(): AsyncIterable<UnifiedDeliveryBatch<T>>;
  historyBatches(
    subscriptionId: string,
  ): AsyncIterable<UnifiedDeliveryBatch<T>>;
  acknowledge(
    batch: UnifiedDeliveryBatch<T>,
    options?: { signal?: AbortSignal },
  ): Promise<DurableConsumer>;
  close(): Promise<void>;
}
BackfillSubscriptionClientOptions@leani/sdk/backfill
export interface BackfillSubscriptionClientOptions {
  baseUrl: string | URL;
  token?: string;
  fetch?: FetchLike;
  /** Deadline for JSON requests; established delivery streams stay open. */
  timeoutMs?: number;
}
BackfillSubscriptionClient@leani/sdk/backfill
export interface BackfillSubscriptionClient {
  readonly baseUrl: URL;
  create(request: CreateBackfillSubscription): Promise<BackfillStatus>;
  list(options?: { signal?: AbortSignal }): Promise<BackfillStatus[]>;
  inspect(
    subscription: string,
    options?: { signal?: AbortSignal },
  ): Promise<BackfillStatus>;
  cancel(
    subscription: string,
    options?: { signal?: AbortSignal },
  ): Promise<BackfillStatus>;
  delete(
    subscription: string,
    options?: { signal?: AbortSignal },
  ): Promise<HistoricalWorkDeletion>;
  changes<T = unknown>(
    subscription: string,
    consumer: string,
    options?: {
      credential?: string;
      limit?: number;
      signal?: AbortSignal;
    },
  ): Promise<{
    data: ChangeEnvelope<T>[];
    nextCursor: string | null;
    coverage: unknown;
  }>;
  stream<T = unknown>(
    subscription: string,
    consumer: string,
    options?: {
      credential?: string;
      batching?: BackfillBatchingRequest;
      signal?: AbortSignal;
    },
  ): Promise<BackfillDeliverySession<T>>;
  streamLive<T = unknown>(
    processor: string,
    consumer: string,
    options?: { credential?: string; signal?: AbortSignal },
  ): Promise<LiveDeliverySession<T>>;
  subscribe<T = unknown>(
    request: MultiLaneSubscriptionRequest,
  ): Promise<MultiLaneDelivery<T>>;
}
createBackfillSubscriptionClient@leani/sdk/backfill
export function createBackfillSubscriptionClient(
  options: BackfillSubscriptionClientOptions,
): BackfillSubscriptionClient;
parseNdjson@leani/sdk/backfill
export async function* parseNdjson(
  body: ReadableStream<Uint8Array>,
  signal?: AbortSignal,
): AsyncGenerator<unknown>;
LeaniErrorBody@leani/sdk
export interface LeaniErrorBody {
  error: {
    code: string;
    message: string;
    retryable: boolean;
    details?: Record<string, unknown> | null;
    requestId: string;
  };
}