bynk-std 0.434.0

The Bynk standard library's first-party sources as data: the `bynk` surface, the Bynk-written commons, the `bynk.cloudflare` platform adapter, and their per-platform TypeScript bindings.
Documentation
adapter bynk {
	uses bynk.locale.types
	exports capability { Clock, Random, Logger, Fetch, Secrets, Locale, Idempotency, Events }
	exports transparent { Uuid, Method, FetchError, Request, Response, EventEnvelope }

	---
	A UUID in canonical 8-4-4-4-12 lowercase-hex form.
	---
	type Uuid = String where Matches("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")

	---
	An HTTP request method.
	---
	type Method = enum { Get, Post, Put, Delete }

	---
	Why an outbound `Fetch.send` failed: a network error, a timeout, or a
	request whose headers `Fetch.send` refused to send (`InvalidHeader`). An
	`InvalidHeader` request never reaches the network: its `headers` named
	Content-Type or Authorization while the matching typed slot was `Some`,
	named a header the platform owns (`host`, `content-length`, and the other
	framing/hop-by-hop headers), named one header twice in different case, or
	held a name or value that is not a legal HTTP header; or a typed slot
	(`contentType`, `authorization`) held a value that is not a legal header
	value — a secret with a stray newline, say. Retrying cannot fix it.
	---
	type FetchError = enum { Network, Timeout, InvalidHeader }

	---
	An outbound HTTP request, as passed to `Fetch.send`. `contentType` and
	`authorization` are the typed slots for the two common headers; `headers`
	carries any other (`User-Agent`, `Accept`, `If-None-Match`, …), and an empty
	map (`Map.empty()`) sends none. Header names are case-insensitive. A
	`headers` entry for Content-Type or Authorization is accepted only while the
	matching typed slot is `None` — when both are given, `Fetch.send` returns
	`Err(InvalidHeader)` rather than let one silently win. Names the platform
	owns — `host`, `content-length`, `connection`, `keep-alive`, `te`,
	`trailer`, `transfer-encoding`, `upgrade`, `expect` — are refused the same
	way, as is a duplicate name differing only in case.
	---
	type Request = {
		method: Method,
		url: String,
		contentType: Option[String],
		authorization: Option[String],
		headers: Map[String, String],
		body: Option[String],
	}

	---
	An HTTP response: a status code, its headers, and a body. `headers` keys
	are lowercased (`res.headers.get("etag")`); a header the server repeated
	arrives as one value, joined with ", ". That includes `set-cookie`, and
	there the join is lossy: a cookie's `Expires` attribute holds a comma, so
	a joined `set-cookie` cannot be split back into its cookies reliably.
	---
	type Response = {
		status: Int,
		headers: Map[String, String],
		body: String,
	}

	---
	Runtime metadata carried alongside an event payload (Events track, slice
	2; design/tracks/events.md, spine #936), available as an `on event`
	handler's optional second parameter. `eventId` is minted once per
	emission — every subscriber of that emission sees the same value, making
	it the dedup key for the `Idempotency` capability's `dedup`/`remember`
	idiom. `publisherId` is the emitting context's qualified name, not the
	emitting agent's instance identity: `Events.emit` is legal from a plain,
	keyless service handler with no agent to report, so a context-scoped
	identifier is the only one available uniformly at every legal emission
	site (an amendment to design/bynk-design-notes.md §7's "the publisher is
	the emitting agent" framing). `schemaVersion` is the version the
	cross-build schema registry (`bynk.schema.lock`, Events slice 3c) computes
	for the emitting event: unchanged shape keeps its stored version, a purely
	additive shape change (only new defaulted fields) auto-bumps it, and a
	non-additive change fails the build. An explicit `@schema(N)` annotation
	(slice 3b) is checked against that computed value rather than trusted
	outright — a mismatch is a build error, not a silent override.
	---
	type EventEnvelope = {
		eventId: String,
		publisherId: String,
		emittedAt: Instant,
		schemaVersion: Int,
	}

	---
	Reads the current wall-clock time, as Unix milliseconds.
	---
	capability Clock {
		fn now() -> Effect[Instant]
	}

	---
	A source of randomness — fresh UUIDs, bounded integers and random bytes.

	`int` draws from a general-purpose generator and is **not** for secrets:
	use `bytes` for tokens, nonces and keys. `bytes(n)` returns `n` bytes from
	the platform's cryptographically secure generator (`crypto.getRandomValues`);
	`n` must be between 1 and 65,536, and anything else fails the effect with an
	error naming the bound.
	---
	capability Random {
		fn uuid() -> Effect[Uuid]
		fn int(lo: Int, hi: Int) -> Effect[Int]
		fn bytes(n: Int) -> Effect[Bytes]
	}

	---
	Structured logging at info and error levels.
	---
	capability Logger {
		fn info(msg: String) -> Effect[()]
		fn error(msg: String) -> Effect[()]
	}

	---
	Performs outbound HTTP requests.
	---
	capability Fetch {
		fn send(req: Request) -> Effect[Result[Response, FetchError]]
	}

	---
	Reads named secrets from the platform's secret store.
	---
	capability Secrets {
		fn get(name: String) -> Effect[Option[String]]
	}

	---
	Reads the current locale for this request/session. Slice 1: always
	returns a fixed reference tag (`"en"`) on every platform.
	---
	capability Locale {
		fn current() -> Effect[LocaleTag]
	}

	---
	Mechanical dedup for at-least-once delivery (design notes §12). `dedup`
	checks for a cached outcome under `key`; on a cache miss the caller
	computes its outcome and writes it back with `remember` (`record` is a
	reserved keyword), which the caller must call to actually cache anything
	— a `dedup` miss with no matching `remember` call simply recomputes every
	time. `expiresAfter` sets the retention window a call to `remember`
	establishes. Slice 0 (design/tracks/idempotency-capability.md): a single
	in-memory provider, lost on process restart — no durability guarantee yet.
	---
	capability Idempotency {
		fn dedup[T](key: String) -> Effect[Option[T]]
		fn remember[T](key: String, value: T, expiresAfter: Duration) -> Effect[()]
	}

	---
	Emits an event declared by the calling context (Events track, slice 0;
	design/tracks/events.md, spine #936). Owner-only: the compiler rejects
	`emit[E]` when `E` is not an event declared in the emitting context, even
	when `E` is otherwise visible via `consumes`. Fire-and-forget: releases at
	handler commit, so an aborted handler emits nothing — a `dedup`/`remember`-
	style write-then-flush the provider itself is responsible for, not a
	distinct control-flow form here. The explicit `[E]` type argument follows
	the `Idempotency.remember[T]` precedent: a generic capability operation's
	type parameter is never inferred, even where an argument's type would
	determine it.
	---
	capability Events {
		fn emit[E](event: E) -> Effect[()]
	}

	provides Clock = ClockProvider

	provides Random = RandomProvider

	provides Logger = LoggerProvider

	provides Fetch = FetchProvider

	provides Secrets = SecretsProvider

	provides Locale = LocaleProvider

	provides Idempotency = IdempotencyProvider given Clock

	provides Events = EventsProvider
}