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

@truewire/core

v0.1.1

Published

Runtime for Truewire-generated TypeScript clients: HTTP and WebSocket transport, codecs, paging, timestamps, errors.

Readme

@truewire/core

Runtime for Truewire-generated TypeScript clients

Every TypeScript client generated by Truewire depends on this package. It holds the parts of a client that are the same for every API — HTTP and WebSocket transport, response codecs, paging, timestamp conversion and the error hierarchy — so generated code only carries what is specific to one API: envelope extraction, error mapping, request signing, wire quirks.

You do not normally install it directly; a generated client lists it as a dependency. You will import from it when catching errors, converting timestamps, or driving a paginated walk.

Installation

npm install @truewire/core

ESM only, Node 22+ (global fetch and WebSocket) or any modern browser. No runtime dependencies. The generator that emits the clients is the truewire CLI (Python, on PyPI); truewire.dev and docs/typescript.md describe the spec format, the generated code and the core contract.

What it provides

| entry point | contents | | --- | --- | | @truewire/core/errors | TruewireError, NetworkError, ValidationError, ApiError (BadRequest, AuthError, RateLimited), LogicError | | @truewire/core/codec | Codec<T> and the combinators (object, array, tuple, union, literal, record, decimal, epochMillis, ...) | | @truewire/core/http | HttpClient over fetch, with NetworkError mapping and wire-level recording() | | @truewire/core/ws | WebSocket base classes: Socket, Streams, Rpc, StreamsRpc, SerialReplies; Stream, Subscription | | @truewire/core/times | EpochConverter, IsoConverter, DateConverter, the Timestamp* aliases and DateIso | | @truewire/core/paging | PaginatedResponse and Page | | @truewire/core/contract | HttpEndpoint<Meta>, CommandEndpoint<Meta>, StreamEndpoint<Meta> and CallOptions: what a generated client asks of its hand-written core | | @truewire/core | everything above; the codec combinators as the t namespace, the socket classes as ws |

Codecs

A generated type is a plain interface plus a codec: an object with parse (decoded wire JSON to the typed value) and dump (typed value back to the wire). The codec is built from a small set of combinators, declared against the interface so tsc proves the two agree:

import { t, type Codec, type Decimal, type TimestampMillis } from '@truewire/core'

interface Order {
  id: string
  amount: Decimal          // "10.50" on the wire, kept as the exact digits, branded
  created: TimestampMillis // 1717072496123 on the wire, a Date in the client
  note?: string
}

const Order: Codec<Order> = t.object({
  id: t.string,
  amount: t.decimal,
  created: t.epochMillis,
  note: t.optional(t.string),
})

const order = Order.parse(JSON.parse(body))   // ValidationError names the path: "expected decimal string, got 10.5 at /amount"
Order.dump(order)                              // { id, amount: '10.50', created: 1717072496123 }

Objects keep keys they were not told about, both ways, so an undocumented field never breaks a client. union tries variants in order (anyOf); record is a map with arbitrary keys (additionalProperties); tuple is prefixItems. The wire formats — decimal, integerString, booleanString, epochSeconds/epochMillis/epochMicros/epochNanos, dateTime, date — parse to Decimal, number, boolean, Date and DateIso, and t.wire builds one of your own. There is no schema interpretation and no eval: the combinators are the whole validator, small enough to ship to a browser.

Errors

Every failure a client throws derives from TruewireError:

import { ApiError, AuthError, NetworkError, RateLimited } from '@truewire/core'

try {
  const order = await client.orders.get({ id: 'ord_123' })
} catch (e) {
  if (e instanceof AuthError) ...        // bad or missing credentials
  else if (e instanceof RateLimited) ... // the API told us to slow down
  else if (e instanceof ApiError) ...    // any other error the API itself returned
  else if (e instanceof NetworkError) ...// couldn't reach the server, or the connection dropped
}

Each class also carries a string code ('auth', 'rate-limited', ...) so an error that crossed a duplicate-bundle boundary can still be told apart.

Paging

Every generated <method>Paged returns a PaginatedResponse: thenable (awaiting gives every row, flattened) and async-iterable (one page of rows at a time, empty pages skipped). Each page is one pure next(state) call, so a caller can retry or resume a single page rather than the whole walk:

const paging = client.orders.listPaged({ status: 'open' })
const orders = await paging                        // every row, flattened
for await (const rows of paging) ...               // one page at a time
for await (const page of paging.pages()) ...       // { rows, state, next }, for checkpointing
paging.resume(savedState)                          // restart from a checkpointed state
paging.via(retried)                                // route every page fetch through a middleware

via(call) hands each page fetch to call as one zero-argument function, so a retry or logging layer wraps a page without unrolling the loop by hand. The contract that makes this safe: next is a pure function of state, state fully determines the request, and the request is a read.

Timestamps

import { EpochConverter, IsoConverter, DateConverter, timestampMillis } from '@truewire/core'

timestampMillis.parse(1717072496123)                       // Date
timestampMillis.dump(new Date())                           // number
new IsoConverter().parse('2024-05-30T12:34:56.123456789Z') // any fraction length, any offset
new DateConverter({ pattern: '%Y%m%d' }).parse('20260101') // '2026-01-01' as DateIso

Timestamps are Dates behind the TimestampSeconds/TimestampMillis/... aliases, so a later move to Temporal is one alias change. Epoch arithmetic is integer (BigInt) throughout: a nanosecond value, even as a numeral string beyond Number.MAX_SAFE_INTEGER, keeps its millisecond digits exactly; the sub-millisecond digits are lost, since a Date has none.

WebSocket

Socket opens lazily on first use and closes on close() (or await using). Rpc correlates replies by id, Streams multiplexes channel subscriptions, StreamsRpc does both on one connection, and SerialReplies matches uncorrelated replies by order. A project's hand-written core is a small subclass implementing parseMsg, rpcSend, requestSubscription and requestUnsubscription:

await using stream = client.streams.ticker({ symbol: 'BTC/USD' })
for await (const tick of stream) ...
// unsubscribed on scope exit

Development

The source lives in packages/core-ts of truewire-dev/truewire; the CHANGELOG lists what each version changed.

yarn install
yarn test        # vitest
yarn typecheck   # tsc --noEmit over src and test
yarn build       # tsc to dist/

License

MIT — see LICENSE.