0Z4C0
// navigation
// @ozaco/std

Codec

A priority-ordered registry of encoders/decoders for bytes, text and streams.

import from@ozaco/std/codec

Codec is a protocol: a scope-local registry of encoders/decoders. Install one or more codec implementations (JsonCodec, TomlCodec, YamlCodec, or your own) and Codec.actions.* transparently route each call to the highest-priority registered codec — ties break toward the most recently installed.

Every codec exposes the same surface: encode/decode work in Uint8Array bytes, stringify/parse work in serialized text, and encodeStream/decodeStream adapt effect Streams. All of them are effect operations that fail with a CodecErrors tag rather than throwing.

The registry is a scope-local effect Context inherited downward through the protocol context — not a global string-keyed table — so it reflects only registrations visible in the current scope chain. Because it lives in std, any consumer (std:fetch, the WebSocket transport, the logger, …) can encode/decode without coupling to a higher layer. A direct SomeCodec.actions.* call always targets that specific codec, bypassing priority routing.

// additional entry points
  • @ozaco/std/codec/impl/json`JsonCodec` — JSON via the built-in `JSON` plus streaming `@streamparser/json`. Default priority `999` (the highest), so installing it keeps JSON as the active codec.
  • @ozaco/std/codec/impl/toml`TomlCodec` — a zero-dependency, from-scratch TOML parser/serializer (adapted from Deno `@std/toml`; datetimes unsupported). Default priority `500`.
  • @ozaco/std/codec/impl/yaml`YamlCodec` — YAML backed by `js-yaml` (an optional peer dependency; install it alongside `@ozaco/std`). Default priority `500`.
01

Protocol

The registry and its routed actions.

const

Codec

The codec protocol — routes `encode`/`decode`/`stringify`/`parse`/stream actions to the highest-priority installed codec.

signature
const Codec: Protocol<
  CodecDef.Context,
  unknown[],
  CodecDef.Actions,
  CodecDef.Handlers
>

// Codec.actions run against the highest-priority installed codec:
Codec.actions.encode(value: unknown): Future<Uint8Array>
Codec.actions.decode<T>(data: Uint8Array): Future<T>
Codec.actions.stringify(value: unknown): Future<string>
Codec.actions.parse<T>(text: string): Future<T>
Codec.actions.encodeStream<T>(
  stream: Stream<T, unknown>,
): Future<Stream<Uint8Array, true | Result.Failure<unknown>>>
Codec.actions.decodeStream<T>(
  stream: Stream<Uint8Array, unknown>,
  json?: boolean,
): Future<Stream<T, true | Result.Failure<unknown>>>

Installing JSON (priority 999) plus TOML/YAML (500) keeps JSON active; install a codec with a higher { priority } to prefer it. The protocol also exposes handler actions (register, unregister, getTransports) used internally by codec implementations to join and leave the registry.

example
import { Codec } from '@ozaco/std/codec'

// Encode/decode through whichever codec currently has priority.
const bytes = yield* Codec.actions.encode({ id: 1 })
const value = yield* Codec.actions.decode<User>(bytes)

// Text and streaming variants share the same routing.
const text = yield* Codec.actions.stringify(value)
const items = yield* Codec.actions.decodeStream(source, true)
const

hasCodec

Whether any codec is registered in the current scope.

signature
const hasCodec: Operation<boolean>

Use this (not Codec.context.get()) to decide whether to auto-install a default codec. It reflects the current scope chain only, not registrations made in unrelated scopes or bundles.

example
import { hasCodec } from '@ozaco/std/codec'
import { install } from '@ozaco/std/plugin'
import { JsonCodec } from '@ozaco/std/codec/impl/json'

if (!(yield* hasCodec)) {
  yield* install(JsonCodec)
}
02

Codec implementations

Built-in codecs, each imported from its own `impl/*` subpath and installed into the registry.

const

JsonCodec

JSON codec backed by the built-in `JSON` and a streaming JSON parser.

signature
// from '@ozaco/std/codec/impl/json'
const JsonCodec: CodecDef // exposes CodecDef.JsonActions; default priority 999

// JsonActions.stringify additionally accepts an indent width:
JsonCodec.actions.stringify(value: unknown, space?: number): Future<string>

Registers itself on setup (name std/json-codec, priority 999) and unregisters on teardown. decodeStream uses @streamparser/json, correctly buffering multi-byte UTF-8 sequences split across chunk boundaries.

example
import { install } from '@ozaco/std/plugin'
import { JsonCodec } from '@ozaco/std/codec/impl/json'

yield* install(JsonCodec)                 // priority 999 (default)
yield* install(JsonCodec, { priority: 1000 }) // or override options

// Direct calls always target this codec, bypassing priority routing.
const text = yield* JsonCodec.actions.stringify(entry, 2)
const

TomlCodec

Zero-dependency TOML codec (from-scratch parser and serializer).

signature
// from '@ozaco/std/codec/impl/toml'
const TomlCodec: CodecDef // exposes CodecDef.Actions; default priority 500

Adapted from Deno @std/toml; TOML datetimes are intentionally unsupported. Registers as std/toml-codec at priority 500, below JsonCodec (999), so installing both keeps JSON the default — register with a higher { priority } to prefer TOML, or install it alone.

example
import { install } from '@ozaco/std/plugin'
import { TomlCodec } from '@ozaco/std/codec/impl/toml'

// Install alone to make TOML the active codec.
yield* install(TomlCodec)
const

YamlCodec

YAML codec backed by `js-yaml` (optional peer dependency).

signature
// from '@ozaco/std/codec/impl/yaml'
const YamlCodec: CodecDef // exposes CodecDef.Actions; default priority 500

Requires js-yaml to be installed alongside @ozaco/std. Registers as std/yaml-codec at priority 500; install it alone or with a higher { priority } to prefer YAML over JSON.

example
import { install } from '@ozaco/std/plugin'
import { YamlCodec } from '@ozaco/std/codec/impl/yaml'

yield* install(YamlCodec, { priority: 1000 }) // prefer YAML
03

Errors

const

CodecErrors

The `std:codec.*` error tags failures are raised with.

signature
const CodecErrors: {
  Encode: 'std:codec.encode'
  Decode: 'std:codec.decode'
  Stringify: 'std:codec.stringify'
  Parse: 'std:codec.parse'
  EncodeStream: 'std:codec.encode-stream'
  DecodeStream: 'std:codec.decode-stream'
  NoCodec: 'std:codec.no-codec'
  NoMatch: 'std:codec.no-match'
}

Codec actions yield* fail(CodecErrors.Encode, message) (etc.) instead of throwing, so callers can pattern-match on the tag.

04

Types

type

CodecDef

The codec plugin shape and its associated action/context/handler types.

signature
type CodecDef = Plugin<CodecDef.Context, unknown[], CodecDef.Actions>

The CodecDef namespace also exposes:

  • Options{ name?: string; priority?: number } passed on install.
  • Context{ name: string; priority: number } stored per registration.
  • Actionsencode/decode (bytes), stringify/parse (text), encodeStream/decodeStream.
  • JsonActions — like Actions, but stringify(value, space?) takes an indent width.
  • Handlersregister/unregister/getTransports, the registry plumbing.
  • EncodeError / DecodeError — the CodecErrors.Encode / CodecErrors.Decode tag types.
05

Constants

Registered symbol used as the protocol subtype tag.

const

CODEC

Subtype tag identifying the codec protocol.

signature
const CODEC: unique symbol // Symbol.for('std:codec')