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

@usearete/sdk

v0.28.0

Published

Pure TypeScript SDK for the Arete Solana streaming platform

Readme

Arete TypeScript SDK

Pure TypeScript SDK for generated stack definitions, program SDKs, prepared operation execution, and chain reads.

Connected clients expose the typed six-route transaction relay as client.transactions. Wallet adapters receive it per invocation from client.transaction and client.inspectOperation, so a shared wallet can safely serve multiple clients. Sessions expose session.transactions and accept a transactions override. HTTP u64 fields are decimal strings on the wire and bigint in the SDK; sends are never automatically retried.

Installation

npm install @usearete/sdk

Quick Start

import { createSession } from '@usearete/sdk';
import { MY_STACK } from './generated/my-stack';

const session = await createSession({
  stacks: { myStack: MY_STACK },
});

for await (const item of session.stacks.myStack.views.MyEntity.list.use()) {
  console.log(item);
}

SDK Shapes

Mental Model

Think of a generated stack as a cartridge: it packages one domain's typed views, queries, programs, reads, and flows, plus any cross-program addresses, constants, defaults, or math. "Cartridge" is only a mental model; the public API continues to use the existing generated stack objects and types.

An Arete session is the console. Insert one or more generated stacks under session.stacks, optionally add standalone programs, and use the console's shared session.programs, session.chain, and session.execute(...) surfaces. Programs packaged by a stack are automatically promoted to session.programs without creating another connection or runtime.

Every semantic operation has an explicit cardinality:

  • instruction - exactly one Solana instruction
  • transaction - exactly one atomic transaction with one or more instructions
  • flow - one or more sequential transactions

Every semantic operation exposes one pure entrypoint:

  • .prepare(input)

Every connected client or session exposes one semantic execution entrypoint:

  • .execute(prepared, options?)

There is no semantic .resolve(), .build(), .stage(), .plan(), or .send() projection surface anymore.

Connected Stack Cartridges

Access a connected generated stack through session.stacks.<name>. Its stable namespaces are:

  • views - typed streaming, list, and state views
  • queries - stack-level HTTP queries
  • programs - owner-scoped program SDKs; the same connected objects are promoted to session.programs
  • addresses - domain address derivations, when defined
  • constants - domain constants and enums, when defined
  • defaults - reusable domain defaults, when defined
  • math - pure domain calculations, when defined
  • read - connected, domain-oriented reads, when defined
  • flows - stack-level multi-transaction operations, when defined

These are namespaces on the existing connected stack object; no separate cartridge class or type is introduced.

Program SDKs

Each packaged, attached, or standalone program is canonically available under session.programs.<name> and exposes these stable namespaces. The owner-scoped session.stacks.<name>.programs.<program> path remains available when disambiguation is needed.

  • programId - the deployed Solana program address
  • schemas - generated validation schemas
  • pdas - exact generated PDA definitions
  • addresses - semantic address derivations, when defined
  • accounts - typed point reads for program accounts
  • queries - program-level HTTP queries
  • read - optional program-oriented convenience reads supplied by the SDK
  • raw - exact IDL instruction builders with synchronous .build(...)
  • instructions - semantic one-instruction operations with .prepare(...)
  • transactions - semantic one-transaction operations with .prepare(...)
  • flows - program-local multi-transaction operations with .prepare(...)
  • constants - program constants and enums
  • defaults - reusable program defaults
  • math - pure protocol calculations

Program Read Transports

Generated account readers use a ProgramReadDescriptor for each program. Local generation emits an explicit local-http descriptor, so the endpoint must come from the connection call:

const client = await Arete.connect(LOCAL_STACK, {
  url: 'ws://localhost:8877',
  httpUrl: 'http://localhost:8877',
});

Installed hosted program SDKs instead carry a complete hosted-binding descriptor opaquely inside the exported program cartridge. Passing that one program object to createSession or ConnectOptions.programs automatically activates its release-pinned account reads; consumers do not import or register the descriptor separately. Hosted account reads ignore httpUrl and stack HTTP endpoints. The advanced programReads override remains available, but it must replace the complete descriptor; release, binding, and endpoint fields are never patched independently.

Stack and program queries remain stack-HTTP operations. They continue to use httpUrl or stack.endpoints.http and do not use a hosted Program Read binding. Composition sessions accept only complete hosted descriptors or complete hosted overrides, and never infer Program Read endpoints from their members.

Hosted Solana Gateway Transports

Hosted stack and program installs embed complete, independent chain and transactions gateway bindings. Arete.connect, standalone program sessions, and generated composition sessions select those authenticated transports by default instead of using a LiveSpec query endpoint. Explicit transports always remain available as overrides:

import { createHostedSolanaGatewayTransports } from '@usearete/sdk';
import { MY_STACK_HOSTED_BINDINGS } from './generated/my-stack';

const { chain, transactions } = createHostedSolanaGatewayTransports(
  {
    chain: MY_STACK_HOSTED_BINDINGS.chain,
    transactions: MY_STACK_HOSTED_BINDINGS.transactions,
  },
  {
    auth: { publishableKey: import.meta.env.VITE_ARETE_PUBLISHABLE_KEY },
  }
);

The helper mints tokens for the exact solana-gateway-binding target. read, transaction:inspect, and transaction:send use separate scope caches. An authentication failure is refreshed at most once, and transaction requests are replayed only when the gateway explicitly reports that upstream dispatch did not begin. Chain and transaction descriptors may intentionally share the same endpoint and binding ID.

Generated hosted compositions additionally retain create<StackName>HostedSession(options) as an explicit convenience helper. The ordinary create<StackName>Session(options) now uses the embedded gateway too. Local/self-hosted compositions carry no gateway descriptor and continue to require caller-supplied chain and transactions transports.

Raw Instructions

Use raw handlers when you want exact wire control and will compose the transaction yourself:

const ix = session.programs.splToken.raw.InitializeMint2.build({
  mint,
  decimals: 6,
  mint_authority: authority,
  freeze_authority: null,
});

await session.transaction([ix]);

Raw builders are the exact IDL escape hatch. Instruction names, account names, argument names, and nested objects retain the generated IDL shape, including snake_case where the IDL uses it.

Semantic Operations

Use semantic operations for normal application code. Their .prepare(...) methods accept semantic, camelCase object inputs, derive routine addresses, normalize amounts, and return prepared artifacts. Use raw only when you deliberately need the exact IDL surface.

Instruction example:

const prepared = await session.programs.tokenMetadata.instructions.createMetadataAccountV3.prepare({
  mint,
  mintAuthority,
  payer,
  updateAuthority,
  name,
  symbol,
  uri,
});

console.log(prepared.kind); // 'instruction'
console.log(prepared.instruction);
console.log(prepared.artifacts);

await session.execute(prepared);

Transaction example:

const prepared = await session.programs.cpAmm.transactions.swap.exactIn.prepare({
  pool,
  payer,
  inputTokenMint,
  amountIn: { ui: '1.25' },
  minimumAmountOut: 1n,
});

console.log(prepared.kind); // 'transaction'
console.log(prepared.transaction.instructions.length);

await session.execute(prepared);

Flow example:

const prepared = await session.programs.presale.flows.escrow.depositPermissionless.prepare({
  presale,
  owner,
  maxAmount: { ui: '1000' },
});

console.log(prepared.kind); // 'flow'
console.log(prepared.plan.transactions.length);

await session.execute(prepared);

Prepared Shapes

Prepared values are immutable and discriminated:

  • PreparedInstruction
  • PreparedTransaction
  • PreparedFlow

All prepared values include:

  • kind
  • name
  • artifacts
  • plan.transactions

Instruction values also include .instruction, and transaction values include .transaction.

Prepared instructions can be composed directly into a transaction without extracting .instruction:

const transaction = createPreparedTransaction({
  name: 'configureMint',
  instructions: [initializeMint, setAuthority],
  artifacts: { mint },
});

Prepared instructions and prepared transactions can also be flattened into one atomic transaction without reaching into their transaction bodies:

const transaction = createPreparedTransaction({
  name: 'createAndConfigureMint',
  operations: [createMint, createMetadata, setAuthority],
  artifacts: { mint },
});

Only single-transaction operations are accepted by operations. A PreparedFlow must retain its ordered transaction boundaries.

Child signer and error metadata is inherited unless the transaction explicitly provides requiredSignerAddresses or errors.

Every successful execution receipt exposes its signatures in transaction order:

const receipt = await session.execute(prepared);
console.log(receipt.signatures);

Instruction and transaction receipts contain one signature. Flow receipts contain one signature for each executed transaction.

Use describePreparedOperation(prepared) for a typed, JSON-safe description, or formatPreparedOperation(prepared) for human-readable text.

const description = describePreparedOperation(prepared);
console.log(JSON.stringify(description, null, 2));

Sessions

Use a session as the console for one or more generated stack cartridges and/or standalone programs behind one execution surface. Stack programs are promoted by reference; session.programs.presale === session.stacks.presale.programs.presale.

import { createSession, createSignerRegistry } from '@usearete/sdk';

const signerRegistry = createSignerRegistry([
  [creatorAddress, creatorSigner],
]);

const session = await createSession(
  {
    stacks: {
      squads: SQUADS_V4_STREAM_STACK,
      presale: METEORA_PRESALE_STREAM_STACK,
    },
    programs: {
      splToken: SPL_TOKEN_PROGRAM,
    },
  },
  {
    transport: 'http',
    endpoints: { http: 'http://127.0.0.1:8081' },
    wallet,
    signerRegistry,
  }
);

const prepared = await session.stacks.squads.flows.vaultProposal.prepare(...);
await session.execute(prepared);

// Signers can also be managed after session creation.
session.signerRegistry.register(memberAddress, memberSigner);

Equivalent entrypoints:

  • createSession(...)
  • Arete.session(...)

Session surface:

  • session.stacks.<name> - connected generated stacks
  • session.programs.<name> - connected packaged, attached, or standalone programs
  • session.chain - canonical generic chain reads
  • session.signerRegistry
  • session.transaction(...)
  • session.execute(...)

Prefer these connected paths in application code. Arete.connect(STACK, ...) remains available when a direct single-stack client is more convenient.

Programs are matched by identity, not by name (compareProgramIdentity(a, b) returns 'same' | 'unproven' | 'different'). A program SDK's identity is its packageReleaseHash, the program package release a registry-installed SDK was generated from; local builds have none.

  • Both have one: equal hashes (or the same object) are the same program. A standalone program that a stack also provides is served by that stack's connected instance: one client, no warning. Different hashes throw AreteError with code PROGRAM_KEY_CONFLICT before anything connects.
  • At least one has none, with the same programSpecHash: the explicitly attached program takes the key, with one console.warn. A standalone session program takes session.programs.<key>, and session.stacks.<name>.programs.<key> keeps the stack's. Two stacks with such copies promote the first stack's.
  • Anything else (a different or missing programSpecHash) throws PROGRAM_KEY_CONFLICT; use session.stacks.<name>.programs.<key> or attach the standalone program under another key. Two stacks bundling different programs under one key both stay reachable through their stacks, and reading session.programs.<key> throws PROGRAM_KEY_CONFLICT naming them.

The same rule applies to withPrograms, ConnectOptions.programs, a session member's programs, and React's useArete(stack, { programs }), where the attached program replaces the stack's for that client. isSameProgramSdk(a, b) is compareProgramIdentity(a, b) === 'same'.

extendProgram, extendPrograms, and withProgramRead drop packageReleaseHash, because a program changed outside its generated SDK is no longer provably that SDK. Generated entries stamp it last with withProgramIdentity(program, { packageReleaseHash }), after the package's own extension.

Generated stack and program objects carry their runtime extensions under registry symbols, so { ...MY_STACK } keeps read, flows, program operations and read descriptors, while Object.keys and JSON never list them. EXTENSION_API_VERSION (also arete.extensionApi in this package's package.json) versions that extension contract and changes only on a breaking change.

Chain Reads

Generic chain reads use the console-level session.chain surface.

Available reads include:

  • exists(address)
  • lamports(address)
  • minimumBalanceForRentExemption(space)
  • clock()
  • account(address)
  • accounts(addresses) - batched account(address), up to 100 per call, results aligned with the input order
  • mint(address)
  • tokenAccount(address)
  • balance({ owner, mint, tokenProgram? })

Example:

const rentLamports = await session.chain.minimumBalanceForRentExemption(82);
const mintInfo = await session.chain.mint(mintAddress);

Streaming Views

Views are still the main streaming surface.

for await (const update of session.stacks.myStack.views.settlementGame.list.watch()) {
  if (update.type === 'upsert') {
    console.log(update.key, update.data);
  }
}

const game = await session.stacks.myStack.views.settlementGame.state.get('game-123');
const latest = await session.stacks.ore.views.OreRound.latest.getOne();

get and getOne open (or reuse) an equivalent subscription, wait for its initial snapshot, and release it. They reject with InitialDataTimeoutError after timeoutMs (5000 by default; null waits forever). getSync only reads a subscription that is already active and returns undefined when there is none.

Every options object is a protocol v2 query with independent ordered membership. Different windows and filters on the same view can run concurrently, while equivalent normalized queries share one reference-counted wire subscription:

const rounds = session.stacks.ore.views.OreRound.latest;

const firstPage = rounds.watch({ take: 10 });
const secondPage = rounds.watch({ take: 10, skip: 10 });

for await (const update of firstPage) {
  if (update.type === 'remove') {
    console.log(`${update.key} left the first-page window`);
  }
}

Completed authoritative snapshots replace membership for their exact query after reconnect. Cursor queries created with after receive incremental snapshots and merge without pruning.

Low-level QueryLease.refresh() returns a Promise<void> that resolves after the refreshed subscription's next complete snapshot is committed. Registration, send, and subscription failures reject and are published on the lease's QuerySnapshot.error, with isRefreshing cleared. Subscriptions created with snapshots disabled resolve after the refresh request is sent.

Connection recovery

autoConnect and autoReconnect default to true and control separate lifecycle phases. Set autoConnect: false to create a disconnected client that the application connects later. Set autoReconnect: false when the application wants to handle post-disconnect recovery itself:

const client = await Arete.connect(MY_STACK, {
  autoConnect: false,
  autoReconnect: false,
});

The same independent options are available per stack member in createSession(...) and on React's AreteProvider.

Caller-supplied schema diagnostics

Core view schemas continue to filter rejected entities. React view hooks additionally accept onSchemaValidationError, which reports { view, key?, entity, error } without changing accepted data.

Safe amount parsing

toRawAmount(input, decimals) throws when user input is invalid. Form and API boundaries can use safeToRawAmount(input, decimals) instead:

import { safeToRawAmount } from '@usearete/sdk';

const result = safeToRawAmount({ ui: amountText }, 9);
if (!result.success) {
  console.error(result.error);
  return;
}

console.log(result.data); // bigint in raw base units

It returns { success: true, data } | { success: false, error } and never throws for an invalid amount input.

Update Types

type Update<T> =
  | { type: 'upsert'; key: string; data: T; cursor?: string }
  | { type: 'patch'; key: string; data: Partial<T>; cursor?: string }
  | { type: 'remove'; key: string; cursor?: string }
  | { type: 'delete'; key: string; cursor?: string };

type RichUpdate<T> =
  | { type: 'created'; key: string; data: T; cursor?: string }
  | { type: 'updated'; key: string; before: T; after: T; patch?: unknown; cursor?: string }
  | { type: 'removed'; key: string; lastKnown?: T; cursor?: string }
  | { type: 'deleted'; key: string; lastKnown?: T; cursor?: string };

remove means an entity left only this query's filter or window. delete means the source entity was deleted and is removed from every query for that view.

A patch for a key the client holds no entity for (never received, or evicted by maxEntriesPerView) is discarded rather than stored as a partial entity; the entity appears with the server's next full upsert. Each discard is reported to onFrameValidationError with reason: 'unknown-key'. Replayable append-view records (frames with an offset) are events and are always applied.

Testing

@usearete/sdk/testing provides supported, dependency-light test helpers:

  • createWebSocketHarness() — a scripted WebSocket server; pass harness.websocketFactory as auth.websocketFactory and answer subscriptions with frames.* builders.
  • createFrameHarness() — the store engine without a socket.
  • createFakeTransactionTransport() — a recording relay that can fail or stall.
  • createWalletFixture() — a recording wallet with scripted outcomes; createTransactionOutcomeFixtures() covers every status and phase.
  • createFetchStub(routes) — a routed, recording fetch.
import { createWebSocketHarness, createWalletFixture } from '@usearete/sdk/testing';

const ws = createWebSocketHarness();
const client = await Arete.connect(MY_STACK, {
  auth: { websocketFactory: ws.websocketFactory },
  wallet: createWalletFixture(),
});

Replay cursors

Updates from an append view backed by the server's journal carry cursor, the {epoch}:{offset} position of the event. Store it with the data you derive from that update and pass it back as after to resume exactly where you stopped — after is exclusive, and it is never a _seq value. State and list views project membership rather than a tape, so their updates have no cursor.

for await (const update of session.stacks.myStack.views.Trade.list.watch()) {
  await db.apply(update, update.cursor); // one transaction: data + position
}

Reconnects resume from the last cursor delivered on each subscription. If a record is lost locally the stream ends with a StreamGapError carrying the last cursor delivered before the loss, rather than skipping records silently. Server-side refusals (cursor-expired, cursor-epoch-changed, cursor-unknown, invalid-cursor, replay-gap, replay-lagged) end the stream with their wire code; isReplayErrorCode identifies them, and the failing frame — including replayWindow and recoverFrom — is on the error's details.

License

MIT