Add comprehensive mapper functions for wire format conversion#2
Merged
Conversation
… compliance The TypeScript client models were consistent with the protocol spec, but mapper coverage was incomplete — only reservationCreateResponseFromWire and metricsToWire existed. This adds all missing request-to-wire and response-from-wire mappers so typed interfaces can be used end-to-end. Also adds validateExtendByMs (spec range: 1–86400000) which was missing. https://claude.ai/code/session_01Sio9KybwwP6J6RYaDJUNNP
Covers all 15 new mapper functions (9 response-from-wire, 6 request-to-wire) and the new validateExtendByMs validation. Brings mappers.ts from 36% to 90% statement coverage and validation.ts to 100%. https://claude.ai/code/session_01Sio9KybwwP6J6RYaDJUNNP
- Programmatic client section now shows typed mapper usage alongside raw wire format - Added wire-format mappers reference table for all request and response mappers - Updated decide, events, balances, and reservations sections with mapper examples - Added release and extend usage examples - Added balance overdraft/debt fields to query examples https://claude.ai/code/session_01Sio9KybwwP6J6RYaDJUNNP
The previous design required callers to use `finally { handle.dispose() }` to
stop the heartbeat. In streaming HTTP handlers, the `finally` block runs when
the handler returns the response object, not when the stream actually finishes,
causing the heartbeat to stop while the stream is still active.
Now commit() and release() automatically stop the heartbeat as terminal
operations. dispose() is retained only for stream startup failures where
neither commit nor release applies.
https://claude.ai/code/session_01Sio9KybwwP6J6RYaDJUNNP
In real streaming code, multiple terminal paths can fire concurrently (onFinish, error handler, abort signal, client disconnect). The handle now enforces once-only finalization: - commit() throws CyclesError if already finalized (silent drops hide bugs) - release() is a silent no-op if already finalized (best-effort by design) - dispose() is a silent no-op if already finalized - handle.finalized exposes the current state This prevents conflicting server-side operations (commit + release on the same reservation) when multiple error/completion paths race. https://claude.ai/code/session_01Sio9KybwwP6J6RYaDJUNNP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a complete set of typed mapper functions to convert between camelCase TypeScript interfaces and snake_case wire format for all API operations. This enables developers to use strongly-typed request/response objects while maintaining compatibility with the wire protocol.
Key Changes
New Response Mappers (snake_case → camelCase)
commitResponseFromWire()— parse commit responses with status, charged, released, and balancesreleaseResponseFromWire()— parse release responses with status and released amountreservationExtendResponseFromWire()— parse extend responses with new expiry timedecisionResponseFromWire()— parse decision responses (ALLOW/ALLOW_WITH_CAPS/DENY)eventCreateResponseFromWire()— parse event creation responses with event IDreservationDetailFromWire()— parse full reservation details with all metadatareservationSummaryFromWire()— parse reservation summaries for list operationsreservationListResponseFromWire()— parse paginated reservation listsbalanceResponseFromWire()— parse balance queries with pagination supportNew Request Mappers (camelCase → snake_case)
reservationCreateRequestToWire()— convert reservation creation requestscommitRequestToWire()— convert commit requests with metricsreleaseRequestToWire()— convert release requests with optional reasonreservationExtendRequestToWire()— convert extend requestsdecisionRequestToWire()— convert decision/preflight check requestseventCreateRequestToWire()— convert event creation requestsHelper Functions
subjectFromWire()andsubjectToWire()— handle subject object conversion with all optional fields (tenant, workspace, app, workflow, agent, toolset, dimensions)actionFromWire()andactionToWire()— handle action object conversion with optional tagsDocumentation & Examples
commit()andrelease()now auto-stop the heartbeatTesting
Validation
validateExtendByMs()function to validate extend duration (1–86400000 ms)Exports
index.tsfor public API accessImplementation Details
stripUndefined()helper to remove undefined fields from wire format, keeping payloads cleanhttps://claude.ai/code/session_01Sio9KybwwP6J6RYaDJUNNP