All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.0.2 - 2026-08-26
Added
- Elixir 1.17 and 1.18 support: the requirement drops from
~> 1.19to~> 1.17. Elixir 1.17 predates the built-inJSONmodule, so consumers on that version add{:jason, "~> 1.4"}to their own deps. The backend is resolved at compile time, so call sites pay nothing for the indirection. @elixir-ts-rpc/react— a TanStack Query adapter for the generated client. TypeduseQuery,useSuspenseQueryanduseMutationper procedure, infinite queries, aqueryKeyPrefixoption,queryFilter/skipTokenhelpers, and theRpcInputOf/RpcOutputOf/RpcErrorOftype helpers.@elixir-ts-rpc/clientnow exports the surface a framework adapter needs:isRpcMethod,AnyRpcMethod,deriveKeyandbuildProxy.@elixir-ts-rpc/reactis built entirely on these, so another adapter can be too.- A documentation site with type-on-hover on every snippet, checked against real codegen output, and a browser playground that runs the actual Elixir codegen compiled to WebAssembly. Links are in the package metadata.
Changed
RpcElixir.Types.FromInferredrequires Elixir 1.19 or later and now says so. Below 1.19 every lookup returns{:error, :no_signature}rather than relying on the library's own version floor.RpcElixir.Types.FromSpecremains the recommended backend on all supported versions.- Codegen no longer uses
:reor selects over the module definition table, so it runs on AtomVM. The TypeScript identifier check inemit_prop_key/1is hand-rolled and slightly stricter than the regex it replaces: Erlang'srelet$match before a trailing newline, and a key ending in a newline is now quoted rather than emitted bare.RpcElixir.HandlerasksModule.defines?/3per function instead of building aMapSetfromModule.definitions_in/2. - Substantially expanded and corrected documentation across every public module, plus dedicated guide pages for custom types, error handling, middleware and plug options.
Fixed
- Compiling the library emitted two
FileSystem.start_link/1 is undefinedwarnings in any project without the optional:file_systemdependency, which is most of them.rpc.gen.ts.watchreferencesFileSystemdirectly, and the library's own build always has the dep, so the warnings only ever appeared downstream. The reference is now marked@compile {:no_warn_undefined, FileSystem}. The runtime check that tells you to add the dep is unchanged. - Middleware-declared error codes could silently vanish from the generated client.
middleware_error_codes/1usedfunction_exported?/3, which only answers for already-loaded modules, so whether a middleware's codes reached the generated types depended on what else happened to load first. The module is now loaded before it is asked.
0.0.1 - 2026-06-18
Added
RpcElixir.Context— request-scoped struct (conn, socket, assigns, private) threaded through middleware and into handlers.RpcElixir.Resolution— wraps a handler call with its context, arguments, result, and error fields.RpcElixir.Types— internal type representation and theinternal_spec()type, plusCustomTypebehaviour for user-defined type mappings.RpcElixir.Types.FromSpec— recommended backend; reads classic@specdeclarations from BEAM debug info viaCode.Typespecand translates them to the internal type map. No compile-time macro required.RpcElixir.Types.FromInferred— experimental backend; reads set-theoretic inferred signatures from theExCkBEAM chunk (Elixir 1.19+, private API, expect breakage on upgrade).RpcElixir.Router— procedure registration with compile-time@specvalidation in__before_compile__, plus thewire_aliasesoption for router-wide wire type substitution (e.g.{DateTime, RpcElixir.UnixMillis}).RpcElixir.Handler—use RpcElixir.Handlercaptures@specASTs into a__rpc_specs__/0accessor so handlers and router can share a Mix project.RpcElixir.Plug— HTTP transport forPOST /rpc/*: JSON decode, dispatch, and response rendering with cookie/header/session draining.RpcElixir.Middleware(+RpcElixir.Middleware.Assign) — request-scoped middleware framework threaded throughContext/Resolution.RpcElixir.Dispatcher— pipeline that performs lookup → input validation → handler invocation → output validation → serialization.RpcElixir.RpcError— structured error struct with:code,:message, and:detailsfields; the dispatcher promotes typed handler errors and codegen maps them to a typedDomainError<Code, Details>alias in TypeScript.RpcElixir.call/4— in-process convenience caller for tests and server-to-server invocations.- TypeScript codegen — the
mix rpc.gen.tstask and the:elixir_ts_rpccompiler emit a fully typed client from a router; a devWatcherregenerates on change. Each generated method carries a JSDoc link to the handler line that produced it, as an absolutefile://URI editors can open. - Branded wire types —
RpcElixir.CustomType'sts_type/0callback for branded string and number wires, andRpcElixir.UnixMillis(aDateTime↔ epoch-millisCustomTypedogfooding the public hatch). - Built-in JSON via Elixir 1.18+'s
JSONmodule (no:jasondependency). Requires Elixir~> 1.19. - Deterministic codegen output — structs, brands, and middleware error codes are emitted in sorted order so regenerated clients produce byte-stable diffs.
Notes
RpcError.detailsvalues must be JSON-native (strings, numbers, booleans, nil, lists, maps). The built-inJSONdoes not auto-encodeDate/DateTime/NaiveDateTime/Time/Decimal; pre-stringify any such values before placing them indetails.