Skip to content

Releases: Cocoanetics/JSONFoundation

JSONFoundation 2.5.0

Choose a tag to compare

@odrobnik odrobnik released this 03 Jul 05:23
4a147fb

The polish release: a full-project review (82 verified findings) applied end to end — bug fixes across the envelope, schema, macro, and transports, a proper import JSONRPC umbrella module, rewritten documentation, and a test suite that grew from 60 to 137 tests. Everything is additive or deprecation-mediated; existing code keeps compiling.

Bug fixes

  • JSON-RPC: a response with a nil result now encodes spec-valid "result": null (and round-trips through this library's own decoder); batches are no longer truncated to their first message by the transports; a malformed batch element surfaces its real decoding error; isBatchPayload tolerates a UTF-8 BOM.
  • JSONSchema: withoutRequired and addingAdditionalPropertiesRestrictionToObjects no longer drop object titles.
  • @Schema macro: non-ASCII text (umlauts, emoji) survives into generated descriptions; a CodingKeys enum is honored at any access level (previously private only), so schema names match wire keys.
  • JSONValue: JSON 0/1 parsed via JSONSerialization no longer misclassify as booleans (NSNumber→Bool bridging, on both Darwin and Linux).
  • TCP: a write to a hung-up peer can no longer kill the host process with SIGPIPE, and mid-stream write failures report a dedicated sendFailed error.
  • Stdio: executable PATH resolution is Windows-aware (; separators, case-insensitive Path, .exe/.cmd/.bat).

import JSONRPC now works

The batteries-included JSONRPC product is backed by a real umbrella module re-exporting the peer, codecs, and the stdio/TCP/SSE transports — no more product/module mismatch.

Renames (old names deprecated, still compiling)

  • MCPJSONCodingJSONCoding
  • StdioMessageTransportStdioTransport
  • JSONValue.valueJSONValue.jsonObject (now public)

Other improvements

  • JSONRPCPeer.sendRequest honors task cancellation; peer/transport contracts (notification ordering, send-after-close) are documented.
  • JSONValue gains uintValue; JSONRPCID gains debugDescription; SSE comment messages round-trip through LosslessStringConvertible.
  • README rewritten around all nine products with runtime examples and Subprocess-trait instructions; doc comments across the previously undocumented public surface; .spi.yml for hosted DocC.
  • CI now builds and tests the Subprocess trait, pins SwiftLint and toolchain actions, and the whole repo is swiftformat/swiftlint --strict clean.

Full Changelog: 2.4.0...2.5.0

JSONFoundation 2.4.0

Choose a tag to compare

@odrobnik odrobnik released this 03 Jul 05:22
3ff56db

Three purely-additive, Foundation-only pieces extracted from SwiftMCP's HTTP/SSE work so they can be shared across LSP / ACP / SwiftMCP.

SSEEventEncoder (JSONRPCWire)

The outbound counterpart of the existing SSEEventDecoder: encodes a data event (optional event/id/retry) or a comment to text/event-stream bytes. Pure, stateless, Data in/out — an SSE server now has a codec symmetric to the client's.

JSONRPCSSEServer (new module)

The transport-agnostic machinery for serving SSE (depends only on JSONRPCWire):

  • SSEStreamHub — a registry of SSE streams with a bounded replay buffer, resume-after-disconnect via Last-Event-ID, retention of disconnected streams, and liveness/force-close. Keyed only by stream id; knows nothing about sessions or JSON-RPC. A synchronous final class by design, so a consumer holds it inside its own isolation (an actor) and calls it atomically.
  • SSEEventID (<uuid>:<sequence> resume anchor), SSEStreamSink (the liveness/close seam a transport adapter supplies), and the SSEMessage / SSEEvent value types.

RequestCorrelator<Key, Reply> (JSONRPCPeer)

A sink-agnostic, key+reply-generic "match replies to the ids I sent" primitive — the channel-independent half of being a JSON-RPC caller. Unlike JSONRPCPeer it owns no transport and performs no send: the caller writes the request itself and uses the correlator only to park and resolve/fail the waiter.

Notes

  • No external dependencies, no platform-floor change (Foundation only). JSONRPCSSEServer is a separate library so a client-only build doesn't pull it.
  • Purely additive; no source-breaking changes.

Full Changelog: 2.3.0...2.4.0

JSONFoundation 2.3.0

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:57
0cc20d6

Rounds out the JSON-RPC transport family with the two simple transports consumers had been hand-rolling, so they now live in the shared package.

LoopbackTransport (in JSONRPCPeer)

A connected in-memory client/server pair (LoopbackTransport.pair()) — what one end writes, the other reads, with no framing. For embedding a server in-process, or for hermetic, subprocess-free peer tests. This is the "loopback transport" the JSONRPCMessageTransport docs already referenced.

let (clientT, serverT) = LoopbackTransport.pair()
let server = JSONRPCPeer(transport: serverT); await server.start()
let client = JSONRPCPeer(transport: clientT); await client.start()
let result = try await client.sendRequest(method: "ping", params: nil)

TCPClientTransport<Framing> (new JSONRPCTCP module)

A TCP client transport to host:port, generic over the wire MessageFraming — the socket sibling of the stdio transports (blocking reader thread + locked blocking write). Built on raw POSIX sockets, so a single implementation serves macOS, Linux and Android (Bionic) with zero external dependency; it's a plain opt-in module, not trait-gated. Connects to a listener only; the listening side (accept loop, Bonjour) stays with platform server stacks. Windows compiles to an unavailable stub.

Other

  • Both transports join the batteries-included JSONRPC umbrella product.
  • No source-breaking changes; purely additive.

Full Changelog: 2.2.0...2.3.0

JSONFoundation 2.2.0

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:30

Add the @Schema macro in its own JSONFoundationMacros target.

Moves the @Schema macro into JSONFoundation alongside the schema model it targets (SchemaMetadata, SchemaPropertyInfo, SchemaRepresentable), so any JSONFoundation consumer gets @Schema without depending on SwiftMCP.

  • New build-time .macro target JSONFoundationMacros (SchemaMacro + the Documentation / String helpers it needs), behind swift-syntax.
  • Public @Schema declaration in the JSONFoundation target.
  • Tests covering conformance synthesis, doc extraction, required/optional, and JSONSchema bridging.

Full Changelog: 2.1.2...2.2.0

JSONFoundation 2.1.2

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:30
1afa8e8

JSONRPCSubprocess: honor ProcessLaunch.environment in the child transport.

The swift-subprocess child transport hardcoded environment: .inherit, silently dropping any caller-supplied ProcessLaunch.environment. The Foundation.Process transport already applies it, and ACP/MCP clients rely on that to inject auth vars (e.g. promoted ACPX_AUTH_* / credentials) into the agents they spawn — so the two transports disagreed and swift-subprocess lost the injected environment.

launch.environment now maps to a full Environment.custom(...) replacement (nil => .inherit); a reduce (not uniqueKeysWithValues) avoids a duplicate-key trap on case-folding platforms. Adds a test spawning a shell with a custom var and checking it round-trips as a JSON-RPC message.

Also: CI deterministically awaits peer replies in tests; Android pinned to 6.3.1.

Full Changelog: 2.1.1...2.1.2

JSONFoundation 2.1.1

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:30

Make AnyCodingKey public (+ a non-failable init) so it can be reused across the package boundary.

Full Changelog: 2.1.0...2.1.1

JSONFoundation 2.1.0

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:30
8a5924d

Add the JSON-RPC runtime — layered onto the existing wire model as logical, opt-in, dependency-free modules.

  • JSONRPCPeer — transport-agnostic correlation + dispatch (pull or push).
  • JSONRPCWire — Content-Length / line framing + SSE event decoder.
  • JSONRPCStdioFoundation.Process stdio transport.
  • JSONRPCSSE — HTTP+SSE client transport (URLSession).

Plus a JSONRPC umbrella product bundling the four. They sit on a small seam, JSONRPCMessageTransport, so the peer is reusable across any wire; LSP, ACP, and MCP-over-stdio differ only in framing (a pluggable MessageFraming) and the SSE client only in being POST-out / stream-in.

Every module is dependency-free, so the package stays zero third-party and keeps its macOS 12 / iOS 15 floor.

Full Changelog: 2.0.0...2.1.0

JSONFoundation 2.0.0

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:30
4928be5

Broaden JSON-RPC params / result to JSONValue — spec-complete. Source-breaking (a 2.0 change).

Request/notification params and response result are now JSONValue? (object, array, primitive, or null) rather than object-only JSONDictionary?. The decoder accepts any JSON value, so a null / non-object result no longer fails to decode — required for peers like ACP (e.g. fs/write_text_filenull).

JSONValue already conforms to the JSON literal protocols, so dictionary-literal construction (params: ["k": .string("v")]) and subscript access (params?["k"]) keep working unchanged; only sites passing a JSONDictionary variable now wrap it in .object(...).

19 tests (incl. null / primitive / array); swiftlint --strict clean.

Full Changelog: 1.3.0...2.0.0

JSONFoundation 1.3.0

Choose a tag to compare

@odrobnik odrobnik released this 27 Jun 07:30
80019eb

Build out the JSON-RPC 2.0 envelope — additive, pure (Foundation-only, zero-dep), no transport or concurrency, no breaking changes.

JSONRPCMessage

  • Accessors method / params / result / error and predicates isRequest / isNotification / isResponse / isErrorResponse / isReply, so consumers stop hand-rolling shape classification.
  • replyOutcome: Result<JSONDictionary?, JSONRPCError>? — the shape a request/response correlator wants.
  • Encoding as the symmetric inverse of decodeMessages: encoded() / encodedString() / encodeBatch(_:).
  • Equatable + Hashable, validate(), and a compact CustomStringConvertible.

JSONRPCID: ExpressibleBy{Integer,String}Literal, intValue / stringValue, description.

JSONRPCError: consolidated into its own file; added serverError(code:message:data:) and isReservedCode / isServerError range checks.

Full Changelog: 1.2.0...1.3.0

JSONFoundation 1.2.0

Choose a tag to compare

@odrobnik odrobnik released this 24 Jun 18:31

JSONValue ergonomics: subscripts and value builders.

Adds convenience helpers to the value layer (folded in from SwiftAgents so every consumer shares them):

  • subscript(key:) / subscript(index:) to read from objects / arrays
  • init(_:) builders from [String: Any] / [Any]
  • a best-effort, non-throwing init(_ value: any Encodable) (asserts on failure; the throwing init(encoding:) remains for callers that need to handle errors)
  • [String: JSONValue].init(jsonObject:)

Full Changelog: 1.1.0...1.2.0