Skip to content

feat: implement the URL search params serialization standard - #553

Merged
razor-x merged 9 commits into
mainfrom
claude/ruby-o5xyjc
Aug 14, 2026
Merged

feat: implement the URL search params serialization standard#553
razor-x merged 9 commits into
mainfrom
claude/ruby-o5xyjc

Conversation

@razor-x

@razor-x razor-x commented Aug 13, 2026

Copy link
Copy Markdown
Member

Adds a complete implementation of URL search params serialization that matches the @seamapi/url-search-params-serializer TypeScript reference implementation byte-for-byte, enabling proper encoding of query parameters in API requests.

Key Changes

  • New Seam::NULL sentinel: Distinguishes three parameter states (omitted, null, value) since Ruby's nil means omitted. Seam::NULL serializes as JSON null in request bodies and empty values in query strings.

  • UrlSearchParams class: Implements WHATWG URLSearchParams interface with:

    • Name/value pair management (append, set, get, delete)
    • UTF-16 code unit sorting (matching JavaScript's URLSearchParams#sort)
    • WHATWG application/x-www-form-urlencoded encoding
  • UrlSearchParamsSerializer module: Serializes Ruby hashes to query strings with:

    • Nested hash flattening via dot notation
    • Array repetition (one pair per element)
    • ECMAScript number formatting (shortest round-tripping digits, exponent notation at 1e21/-7)
    • Proper handling of special values (floats, dates, booleans)
    • Comprehensive validation with UnserializableParamError
  • UrlSearchParamsEncoder: Faraday integration that applies the serializer to query params while preserving caller-built query strings verbatim.

  • Conformance testing: Added comprehensive test infrastructure:

    • generate_fixture.rb: Creates 200+ test cases covering edge cases, special characters, UTF-8, dates, and error conditions
    • run_reference.mjs: Runs TypeScript reference implementation
    • run_ruby.rb: Runs Ruby implementation
    • roundtrip.mjs: Validates Ruby output round-trips through the parser
    • run bash harness: Orchestrates full conformance suite
  • Documentation: Updated README with Seam::NULL usage examples and URL search params serialization details.

Notable Implementation Details

  • Float formatting exactly matches ECMAScript Number::toString behavior
  • UTF-16 code unit sorting handles astral plane characters (surrogate pairs) correctly
  • Empty arrays serialize as single pair with empty value (not omitted)
  • Nil parameters are omitted entirely; only Seam::NULL serializes as empty
  • Request path resolution preserves serialized query string verbatim
  • Comprehensive error messages distinguish parameter validation failures from API rejections

https://claude.ai/code/session_017p9TwdqYv9ccnv7nGvpfsp

claude added 4 commits August 13, 2026 19:06
Port @seamapi/url-search-params-serializer to Ruby: a UrlSearchParams
pair collection with WHATWG form encoding and UTF-16 code unit sort, a
serializer with ECMAScript number formatting and JavaScript toISOString
date formatting, and the Seam::NULL sentinel distinguishing an explicit
JSON null from an omitted parameter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017p9TwdqYv9ccnv7nGvpfsp
Query params are built by a Faraday params encoder that applies the
serializer, so requests carry the exact encoding, ordering, and number
formatting the Seam API parses; Faraday's default encoder dropped the
'=' from empty-array params, silently turning an empty filter into no
filter. Request bodies replace the Seam::NULL sentinel with nil before
JSON encoding, and generated methods now document nullable parameters
with Seam::Null in their types, consuming the blueprint isNullable flag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017p9TwdqYv9ccnv7nGvpfsp
Verify the Ruby serializer byte for byte against the TypeScript
reference implementation over hand-built, structural-fuzz, and
float-fuzz fixtures, and round-trip the output through
@seamapi/url-search-params-parser in strict mode. The harness needs
Node and the reference implementation, so it lives in dev/conformance
outside the shipped gem.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017p9TwdqYv9ccnv7nGvpfsp
The serializer standard is stable, so the Node-based comparison against
the TypeScript reference and its devDependencies are not worth carrying.
The RSpec suite covers the spec directly, including the exact ECMAScript
exponent formatting and multi-byte encoding cases the harness verified.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017p9TwdqYv9ccnv7nGvpfsp
@razor-x
razor-x marked this pull request as ready for review August 13, 2026 23:46
Match seamapi/python#617: the SDK serializes query params with strict
mode enabled, appending _strict=true after the sorted pairs of any
non-empty query so the Seam API uses strict, schema-aware parsing. The
strict entry points live in strict_url_search_params_serializer so the
base serializer stays a faithful port of the standard, and a query
built entirely by the caller still passes through without it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017p9TwdqYv9ccnv7nGvpfsp
@razor-x razor-x changed the title Implement URL search params serialization conforming to standard feat: implement the URL search params serialization standard Aug 14, 2026
@razor-x
razor-x merged commit 2cb5675 into main Aug 14, 2026
17 checks passed
@razor-x
razor-x deleted the claude/ruby-o5xyjc branch August 14, 2026 05:55
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.

2 participants