Skip to content

Relay API reference: TypeSpec→AsyncAPI 3.0 tooling + full Relay surface + Fern docs#418

Open
Devon-White wants to merge 25 commits into
mainfrom
Devon/relay-asyncapi-tooling
Open

Relay API reference: TypeSpec→AsyncAPI 3.0 tooling + full Relay surface + Fern docs#418
Devon-White wants to merge 25 commits into
mainfrom
Devon/relay-asyncapi-tooling

Conversation

@Devon-White

Copy link
Copy Markdown
Collaborator

Summary

Documents the SignalWire Relay API (JSON-RPC 2.0 over WebSocket / BLADE) as a rendered WebSocket reference in Fern, authored in TypeSpec via a custom TypeSpec → AsyncAPI 3.0 emitter.

  • Tooling@signalwire/typespec-asyncapi emitter (mirrors @typespec/openapi3): walks the program, synthesizes the JSON-RPC request/reply envelopes + the signalwire.event carrier, emits valid AsyncAPI 3.0. Conformance-gated by the official AsyncAPI CLI (run as an isolated subprocess). This branch also: wired the output-file option so each service emits its own channel spec, and fixed a silent fidelity bug where a property typed as a named union/model dropped its description/default (now attached to the $ref via an allOf wrapper). 21 emitter unit tests pass.
  • Full Relay surface authored in TypeSpec — 6 services / 71 methods / 26 events: signalwire handshake (5+1), calling (61+21), messaging (1+2), tasking (1+1), provisioning (1+0), webrtc Verto pass-through (2+1). Every generated fern/apis/relay/<svc>.yaml validates 0 errors against the official AsyncAPI CLI.
  • Fern integration — new SignalWire Relay entry (api-name: relay) in fern/products/apis/apis.yml. The old OpenRPC jsonrpc/calling-rpc tab stays commented (superseded).

Discriminated unions are emitted as AsyncAPI allOf-inheritance (not oneOf+discriminator, which crashes Fern 5.44.4's link indexer) — verified rendering live.

Test plan

  • cd specs/emitters/typespec-asyncapi && yarn test → 21 pass (incl. official-CLI conformance)
  • yarn workspace signalwire-docs-specs build:relay compiles all 6 services
  • each fern/apis/relay/<svc>.yamlnpx @asyncapi/cli@6.0.2 validate reports 0 errors
  • yarn fern-check clean (aside from the environment-only FDR-403 network check)
  • yarn start:dev/docs/apis/relay/<svc>/<svc> renders Send/Receive ops, JSON-RPC message examples, the signalwire.event carrier, and Bearer auth (verified for messaging + calling channels)

Follow-ups (non-blocking)

  • Fern shows the handshake URL as wss://host/<channelId> rather than the server /api/relay/wss pathname for null-address channels — cosmetic.
  • Just-in-time verification debts on a few under-documented families (calling.pay wire types, calling.ai/amazon_bedrock param surface, hold/unhold marked NOT IMPLEMENTED, live_transcribe/live_translate action sub-shapes) — modeled loosely, resolve against FreeSWITCH C source when those families need full fidelity.

Also fixes test harness: import the library and use the full
SignalWire.AsyncAPI namespace path so decorators resolve.
…uth security

Avoids depending on @typespec/http (whose lib tsp won't load in the
hoisted standalone tester); custom @bearerAuth keeps the emitter self-contained.
…dModel, inline-param docs

Addresses adversarial review: numeric enum/discriminator type inference (latent
invalid-schema bug), Array.isArray guard, isNamedModel helper, inline-parameter
description extraction, and a multi-method/multi-event collision-coverage test.
String-discriminator output is unchanged (golden snapshot + Relay artifact in sync).
@minValue/@maxValue/exclusive, @minLength/@maxlength, @minItems/@Maxitems,
@pattern, @Format, @secret→format:password, property defaults, and @example
now map to the corresponding JSON-Schema keywords (valid per AsyncAPI 3.0's
Draft-07 superset). Relay timeout now models its non-negative/default-30 semantics.
…odedname

Completes the standard property-decorator surface: @encode decays to its wire
type with a JSON-Schema format (e.g. utcDateTime+rfc3339 → string/date-time),
@Encodedname remaps the emitted JSON property key. No standard property decorator
is silently dropped now.
…r assertions, dup-method diagnostic

Addresses the remaining adversarial-review quality findings:
- Replace SchemaObject=Record<string,unknown> with a structured AsyncAPISchema
  interface (correct AsyncAPI/Draft-07 dialect: numeric exclusiveMin/Max, string
  discriminator, no nullable) — removes the as-Record casts. Not reusing
  @typespec/openapi3's OpenAPI3Schema, which models a different dialect.
- RefFn returns SchemaOrRef (= AsyncAPISchema | AsyncAPIRef); unionInline typed
  to Union (removes the as-any casts).
- emitRpcMethods/emitEvents take a typed EmitTarget context instead of drilling
  doc!.components! — removes non-null assertions.
- Add duplicate-@rpcMethod diagnostic + test.
…p invented /api/relay/wss path)

Verified against the TS + Python SDK clients: both connect to `wss://${host}` with
DEFAULT_RELAY_HOST = relay.signalwire.com and reject a host containing a path. The
service is selected by the JSON-RPC method in the payload, not a URL path.
With address null, Fern composes the channel URL as wss://<host>/<channelId>,
showing a misleading per-service path (e.g. wss://relay.signalwire.com/messaging).
Relay multiplexes every service over one root connection and routes by the JSON-RPC
method, so emit the root address "/" — Fern then renders wss://relay.signalwire.com/.
Verified live for the messaging and calling channels.
…pecs

The emitter's dist/ is gitignored and nothing built it in CI, so build:relay
failed with import-not-found on ../dist/src/index.js. build:relay now runs the
emitter's tsc build first (verified against a clean dist/).
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

The Relay API was rendering in the same flat sidebar as REST. Convert the apis
product nav to tabs (matching the compatibility-api pattern): a REST tab and a
Relay tab. URLs are preserved — Core stays at /docs/apis/<page>, REST at
/docs/apis/rest/..., Relay at /docs/apis/relay/... (tab carries the slug, the API
uses skip-slug). Verified live: both tabs render and all existing URLs 200.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant