s2-sdk-typescript
The official TypeScript SDK for S2's durable streams API - append, read, and tail records with typed, retryable sessions.
Repository Health
Technical Analysis
@s2-dev/streamstore is the official TypeScript client for S2, a serverless, managed data store built specifically for durable, unlimited streams. Rather than wrapping a generic database, the SDK models S2’s own primitives directly: accounts own basins (namespaces), basins own streams, and streams are append-only logs that can be written to, read from by sequence number or timestamp, and tailed live for new records.
The SDK layers ergonomic, high-level constructs on top of S2’s REST API: an AppendSession for ordered, pipelined writes with backpressure; a Producer with automatic request batching and lingering for high-throughput producers; and readSession for streaming reads that can follow a stream’s tail indefinitely. It ships configurable retry policies (including an idempotency-aware policy for safe replays), automatic transport selection between fetch-based HTTP/1.1 and an HTTP/2 streaming transport (s2s), and works across Node.js, Bun, Deno, and browsers.
It is a thin, well-typed layer purpose-built for one service - not a general message-queue or database client - so every abstraction (basins, streams, append acknowledgements, caught-up tracking) maps directly onto an S2 concept.
What You Get
- Full client surface -
S2top-level client plus scoped helpers for basins, streams, access tokens, locations, and metrics. - AppendSession - a stateful, ordered append channel with backpressure via
maxInflightBytes, returning tickets you can await for durability acknowledgements. - Producer with auto-batching -
Producer+BatchTransformlinger and coalesce records into larger batches automatically for high-throughput writers. - Read sessions with live tailing -
readSessionsupports async iteration, sequence/timestamp-based starts, and open-ended following of new records. - Configurable retries - global
RetryConfigwithappendRetryPolicy: "noSideEffects" | "all"to control whether retries can duplicate non-idempotent appends. - Dual transport with HTTP/2 pooling - automatic detection between fetch (HTTP/1.1) and
s2s(HTTP/2), with connection pooling and flow-control handling for the streaming transport.
Common Use Cases
- High-throughput event ingestion - using
Producerto batch and append large volumes of events durably with minimal per-record overhead. - Ordered write pipelines - using
AppendSessionwhere downstream consumers depend on the exact order records were submitted. - Live tailing consumers - opening a
readSessionwith an open-ended stop condition to process new records as they land. - Point-in-time or replay reads - reading from a specific sequence number or timestamp to reprocess a range of a stream’s history.
- Cross-runtime streaming clients - shipping the same SDK code to a Node/Bun backend and a browser client, relying on automatic transport fallback.
Under The Hood
Architecture
The client is organized around S2’s own domain model: S2 is the account-level entrypoint composing S2Basins, S2AccessTokens, S2Locations, and S2Metrics; calling .basin() yields an S2Basin scoped to basin-level operations, and .stream() on that yields an S2Stream for data-plane appends and reads (packages/streamstore/src/s2.ts, basin.ts, stream.ts). Data-plane primitives are layered rather than flat: raw append/read calls sit underneath higher-level AppendSession/Producer and readSession abstractions (producer.ts, batch-transform.ts) that add ordering, batching, and backpressure without changing the underlying wire protocol. A generated client layer (src/generated/client, src/generated/proto) isolates REST/protobuf codegen from the hand-written ergonomic API, so regenerating from S2’s OpenAPI/proto specs does not touch the public surface directly.
Tech Stack
Written in TypeScript, built with tsc into separate ESM and CJS outputs and verified with @arethetypeswrong/cli for package-export correctness. Runtime dependencies are minimal - @protobuf-ts/runtime for the binary protocol path and debug for diagnostic logging - keeping the installed footprint small. The monorepo (managed with Bun workspaces and Changesets for release versioning) also houses companion packages (patterns, resumable-stream) and generates its REST client via @hey-api/openapi-ts and its protobuf bindings via @protobuf-ts/plugin, with deno.json/deno.lock present for explicit Deno compatibility testing.
Code Quality
The package has an extensive Vitest test suite covering unit behavior (retry policies, batching, session state machines, error classification) and end-to-end scenarios (*.e2e.test.ts against a live or emulated S2 backend), including targeted regression tests tied to specific numbered GitHub issues. Error handling is explicit and typed - error.ts classifies connection failures across Node, Chrome, Firefox, and Safari error message formats and normalizes them into a typed S2Error/retry-eligibility model rather than swallowing failures. The codebase is linted and formatted with Biome, uses strict TypeScript throughout, and CI configuration lives under .github/.
What Makes It Unique Unlike general-purpose queue or database SDKs, this client is built end-to-end around one narrow, well-defined service model - durable, unlimited append-only streams - which lets it offer stronger guarantees (ordering, caught-up tracking, idempotency-aware retries) than a generic wrapper could. Its dual-transport design, automatically choosing between a simple fetch-based path and a pooled HTTP/2 streaming transport with explicit flow-control handling, is a comparatively rare piece of engineering for a client SDK at this scale, and its cross-runtime support (Node, Bun, Deno, browsers) is validated directly in the test suite rather than assumed.