@browsercore/contracts
v0.2.3
Published
Shared interfaces, models, and options for the browsercore stack
Readme
@browsercore/contracts
The canonical interface package for the browsercore stack. Every protocol
package, runtime adapter, and consumer depends on these contracts — they
define how components communicate without coupling them to each other. This is
now the single source of truth for all cross-package interfaces, including
the Platform service contracts (events, time, telemetry, network, crypto,
compression).
Why this package exists
Zero drift through global interface distribution. Without a shared
contracts package, every @browsercore/* package would define its own version
of Transport, BrowserProfile, FetchClient, etc. When one package changes
a shape, the others silently break at runtime. By centralizing every
cross-package interface here, a type error becomes a compile error the moment
any package diverges.
Alternative implementations welcome. The contracts define exactly what a
component must satisfy — nothing more. Want to write a different TLS engine?
Implement TlsConnection. A different transport? Implement Transport. A
different runtime (Bun, Deno, Workers)? Implement the Platform service
interfaces. The contracts are the spec; the packages are the reference
implementations.
Implementation-independent. No node:crypto, node:net, Buffer, or any
runtime details leak into these types. They are portable TypeScript interfaces
and plain data.
Architecture
@browsercore/contracts (this package — interfaces + services + models)
▲
│
┌──────┼─────────────────────────────┐
│ │ │
▼ ▼ ▼
transport tls http1 http2 http3 quic fetch crypto compression
│ │ │ │ │ │ │ │ │
└─────────┴──────┴───────┴───────┴───────┴───────┴────────┴─────────┘
▼
browsersmith (composition root — builds Platform)Dependency direction is strictly upward — contracts is the root dependency.
browsersmith is the only package allowed node:* imports; it builds a
Platform object that threads runtime capabilities (network, crypto,
compression, events, telemetry, time) down through options.
What's inside
| Module | Purpose | Key exports |
|---|---|---|
| Contracts | Provider + connection interfaces that protocol packages implement | CryptoProvider, CompressionProvider, Transport, DatagramTransport, TlsConnection, Http1Connection, Http2Connection, Http3Connection, QuicConnection, QuicStream, FetchClient, CookieJar, PacketCallback |
| Platform | The composition root + per-service bundles | Platform, PlatformOptions, Network, Crypto, Compression |
| Events | EventTarget-backed emitter abstraction | EventProvider, TypedEventEmitter<T> |
| Time | Clock + scheduler with composable deadlines | Clock, Duration, Scheduler, Deadline, Time |
| Telemetry | OTel-aligned observability | Logger, Tracer, Span, Metrics, Telemetry |
| Models | Shared data structures that cross package boundaries | BrowserProfile, TlsProfile, Http1Profile, Http2Profile, Request, Response, Headers, Cookie, TransportState, CloseReason, ContentEncoding |
| Net | Platform-agnostic TCP + DNS contracts for runtime portability | Net, DnsResolver, Socket, ConnectOptions, IPAddress |
| Options | Configuration objects passed to each protocol package | TlsOptions, Http1Options, Http2Options, Http3Options, QuicOptions, FetchClientOptions |
| IANA Tables | Canonical TLS wire code lookup tables (single source of truth) | CIPHER_SUITE_CODES, NAMED_GROUP_CODES, SIGNATURE_SCHEME_CODES, VERSION_CODES |
Usage
Importing types (compile-time only)
import type { Net, DnsResolver, Transport, BrowserProfile } from "@browsercore/contracts";Type imports are erased at compile time — zero runtime cost.
Importing wire code tables (runtime)
import { CIPHER_SUITE_CODES, NAMED_GROUP_CODES } from "@browsercore/contracts";
const aes128 = CIPHER_SUITE_CODES["TLS_AES_128_GCM_SHA256"]; // 0x1301
const x25519 = NAMED_GROUP_CODES["x25519"]; // 0x001dImplementing an alternative package
import type { Transport, TransportState, CloseReason } from "@browsercore/contracts";
class MyCustomTransport implements Transport {
// Implement the interface — the rest of the stack works unchanged
}Design rules
If a type crosses a package boundary, it lives here. Litmus test: "Could someone build an alternative package against it?" If yes → contracts. If only one package uses it → stays in that package.
No runtime behavior. Only TypeScript types and
constdata tables. No classes with logic, no functions with side effects.No Node built-ins. Types reference only portable primitives:
string,number,Uint8Array,Promise,IterableIterator.Branded types for IDs.
ProfileId,StreamId,ConnectionIdetc. are opaque branded types, not barestring/number.Discriminated unions for state.
TransportState,CloseReason,TlsStatemodel every valid state explicitly — invalid combinations are unrepresentable.
Zero overhead
All type exports are erased by the TypeScript compiler. The only runtime
exports are the IANA wire code tables and Duration factories — plain
const objects with no dependencies, no side effects.
