Skip to main content

Module wire

Module wire 

Source
Expand description

#855: the wire-contract IR — the shape a Bynk type takes crossing a context boundary, derived once from the AST + type table so both the emitter’s codec generation and the editor’s “wire contract” peek render the same derivation instead of two hand-synchronised ones.

The seam: bynk-check says what the boundary is. bynk-emit says how that reads as TypeScript.

Crossing this module boundary is legal for BaseType, PredKind, TypeRef, Expr, TypeDecl (all bynk-syntax) — the vocabulary a boundary type is built from. It is not legal for any TS-token string, pred_condition_and_message (bynk-emit/src/emitter.rs, stays put), lower_field_default_wire (bynk-emit/src/emitter/serialisation.rs, stays put — a default’s wire literal is a rendering, not a boundary fact), or the Qual type-name→TS-namespace-prefix map (bynk-emit’s serialisation.rs; see the Provenance doc for why it cannot move here). bynk-emit renders this IR into TypeScript; it does not re-derive it.

Derived from AST + type table, not checker::Ty. Ty::Named { name, kind, args } carries no refinement predicates, so a Ty-based derivation would still need this same type-table lookup for PredKinds — Ty would buy only generic substitution, which the moved walks below already implement directly over TypeRef. contract.rs (this crate) also derives straight from the AST + type table and must not depend on checker output being available; this module keeps the same shape of dependency.

This IR is not contract.rs’s canonical form, and the two must never be unified — they disagree on purpose, on every axis that matters:

contract.rs (hash)wire.rs (this module)
predicatessorted, dedupeddeclaration order, not deduped
record fieldssorted by namedeclaration order (emitted key order)
sum variantssorted by namedeclaration order (switch arm order)
opaque predicateelided — unobservable to the consumerpresent — the owner still re-validates

contract.rs:248 documents the sorting as a precondition for hash correctness — hashing predicates in source order “would make two contexts that agree perfectly fail closed against each other.” The hash is a type identity (order-insensitive by design); this module’s shapes are an emission order (order-sensitive by necessity, since a JS switch and an inlined if chain both have a literal source order a reader can see). Merging them would create the exact spurious-409 failure the hash exists to prevent. What the two genuinely share — and what a cross-check test elsewhere asserts — is boundary-type reachability, not shape.

Structs§

WireField
One field of a boundary record, in declaration order (the emitted JSON key order).
WireModel
The set of TypeRefs a boundary walk resolved and their re-derived structural shapes — the single source of truth both the emitter’s codec generation and the wire-contract peek render from.
WireScalar
A refined- or opaque-base-type boundary scalar.
WireSum
A boundary sum type. The wire and in-memory discriminants are carried side by side because the codec’s whole job is translating between them — memory_discriminant is the softest part of this seam (a host representation choice, not a wire fact); moving it back into bynk-emit alone is a one-field change if a reviewer objects.
WireType
A named boundary type’s declared shape, refinement-independent of who is asking. owner-relative facts (Revalidation) live one level down, in WireScalar — the shape itself does not vary with provenance, only how hard the receiver re-checks it.
WireVariant
One variant of a WireSum, in declaration order (the emitted switch arm order).

Enums§

BaseGuard
An extra structural guard a base-typed wire value must pass beyond its typeof: an Int/Instant must be whole, a Float must be finite.
Expected
What a structural-mismatch error reports as expected — the vocabulary a renderer needs to explain why a wire value was rejected, independent of the TS spelling of the check that rejected it.
JsonKind
The JSON value shape a BaseType occupies on the wire. Replaces bynk-emit’s ts_base_for_serialisation classification — the TS-token spelling of each kind ("number", "string", …) stays in bynk-emit, since a TS token is exactly what this seam excludes.
Provenance
Where a boundary type’s declaration lives relative to the module doing the crossing.
Revalidation
#661 Decisions C/D plus the owner path, stated once: how a boundary scalar’s refinement is re-checked on the way in, given who declared it and where the check is happening.
UncheckedReason
Why a WireRef carries no generated codec — the runtime-owned error family, which has no TypeDecl to derive a shape from.
WireBody
WireInst
A boundary occurrence of a generic instantiation needing its own specialised codec — Result<A, B>, Option<A>, List<A>, Map<K, V>, or a generic user record/sum applied to concrete arguments (Paginated[User], ApiResult[User]).
WireRef
The resolved shape of one TypeRef occurrence — a field’s type, a sum variant payload’s type, a generic instantiation’s argument. Mirrors the exhaustive dispatch bynk-emit’s emit_field_deserialise performs today, one level removed from any TS string.

Functions§

boundary_model
Build the full WireModel for a boundary: every non-generic named type in type_names resolved through types, plus the generic instantiations the caller already collected (collect_generic_instantiations / collect_codec_closure). provenance is supplied by the caller — bynk-emit knows the build target and the consumed set; this crate must not (see the Provenance doc).
codec_suffix
The codec-name suffix a TypeRef resolves to — Int, Order, Result_Int_String, Paginated_User. Renamed from inner_ts_name in the move from bynk-emit; used both to key a WireInst and, unqualified, as the bare codec function suffix a same-module call reaches.
collect_boundary_types
Compute the set of type names (transitively reachable) that need serialise/deserialise helpers for this context: any type used in the argument or return position of a service handler exposed by this context, walked through record fields, sum payloads, and the generic type parameters of Result/Option/Effect.
collect_codec_closure
v0.22b: the codec closure for a set of Json.encode/Json.decode[T] target type-refs — the named types needing per-type helpers (transitively through record fields and sum payloads) plus the generic instantiations needing specialised helpers. The same closure logic as the boundary collectors, rooted at expressions instead of service signatures.
collect_generic_instantiations
Collect the set of Result<A, B> / Option<A> instantiations used in boundary positions so the emitter can synthesise the specialised helpers. v0.18: an instantiation may also appear in the fields of a boundary record or sum payload (e.g. the bynk surface’s Request.contentType: Option[String]) — the per-type serialisers delegate to the specialised generic helpers, so walk those too.
inst_codec_suffix
v0.174 (#592): the monomorphised codec suffix for a generic-record instantiation — Paginated[User]Paginated_User, Pair[User, String]Pair_User_String. #593: shared with generic sums. Renamed from app_ts_name in the move from bynk-emit.
json_kind_of
The JSON kind a base type wires as. Same mapping as the ts_base_for_serialisation it replaces: Int/Float/Duration/InstantNumber (all four erase to a TS number), String/BytesString (a Bytes wires as a base64 string, ADR 0142 D5), BoolBoolean.
record_inst_fields
v0.174 (#592): the concrete (field-name, field-type) list for a generic record instantiation Name[args…] — the declared fields with every type parameter substituted by the matching argument. Returns None if name is not a declared generic record or the arity does not match (both guaranteed impossible by the checker, so this is purely defensive).
sum_inst_variants
#593: the concrete (variant-name, [(field-name, field-type)]) list for a generic sum instantiation Name[args…] — the declared variants with every type parameter substituted by the matching argument. The sum analogue of record_inst_fields; None (defensively) if name is not a declared generic sum or the arity does not match.
wire_ref
Resolve one TypeRef occurrence (a field’s type, a variant payload field’s type, a generic argument) into its WireRef shape — the same dispatch emit_field_deserialise performs today, one level removed from any TS string.
wire_type
Build a WireType from a TypeDecl, using the same base/predicate/ opaque logic currently inline in bynk-emit’s emit_refined and emit_bytes_named_codec — without any TS string emission. Returns None for a generic declaration (decl.type_params non-empty): a generic record/sum has no single bare WireType of its own, only per-instantiation shapes (WireInst), mirroring the skip in emit_helpers_for_owner_qualified.