Skip to content

feat(http): stabilize structured fields - #7232

Open
tomas-zijdemans wants to merge 6 commits into
denoland:mainfrom
tomas-zijdemans:stabilizeStructured
Open

feat(http): stabilize structured fields#7232
tomas-zijdemans wants to merge 6 commits into
denoland:mainfrom
tomas-zijdemans:stabilizeStructured

Conversation

@tomas-zijdemans

Copy link
Copy Markdown
Contributor

Stabilizes the RFC 9651 Structured Field Values module (added in #6963).

  • Renames unstable_structured_fields.tsstructured_fields.ts; export is now @std/http/structured-fields
  • Renames the Parameters type to ParameterMap, as it shadowed TypeScript's built-in Parameters<T> utility type
  • Removes @experimental tags and exports the module from mod.ts
  • Updates unstable_message_signatures.ts imports accordingly

The module passes the official HTTPWG conformance suite and has had no behavioral changes since landing.


Note

Recreates #7207, which was closed automatically when I accidentally deleted the head fork. The branch is identical to the original PR head.

@github-actions github-actions Bot added the http label Jul 15, 2026

@bartlomieju bartlomieju left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The question I most wanted answered before freezing this API was whether we'd be stabilizing an RFC 8941-shaped surface that would need a break to pick up RFC 9651's Date and Display String types — and the answer is no, this is already 9651 throughout. date() and displayString() are first-class, BareItem carries both variants, and the date.json / display-string.json conformance vectors run. Since BareItem is a discriminated union, any future RFC type is additive. That's the right footing to stabilize on.

Process matches the prior stabilizations (fe0fb93, b97383d) exactly: rename, deno.json remap, @experimental removal, mod.ts re-export, and dropping the old export path without an alias, which is our established precedent for unstable APIs.

Inline notes below. The mod.ts one is the only thing I'd genuinely like reconsidered before merge, since it's the one decision here we can't walk back.

Comment thread http/mod.ts Outdated
export * from "./negotiation.ts";
export * from "./server_sent_event_stream.ts";
export * from "./server_sent_event_parse_stream.ts";
export * from "./structured_fields.ts";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the piece I'd reconsider before merging, because it's permanent once shipped.

The wildcard hoists string, token, date, boolean, integer, decimal, binary and item — plus the types Item, List and Dictionary — into the root @std/http namespace. Those are about as generic as identifiers get, and anyone doing import * as http from "@std/http" inherits all of them permanently.

The strongest evidence is in this very PR: unstable_message_signatures.ts:70-79 imports them as sfString, sfToken and sfInnerList, because the bare names don't survive contact with surrounding code. If our own sibling module has to rename them on import, callers will too.

I'd suggest leaving this module subpath-only — the same treatment unstable_cache_control and friends get. Users reach it via @std/http/structured-fields, which reads better at the call site anyway.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, fixed in 9301b98. Subpath-only now, and structured_fields.ts is on the check_mod_exports skip list so CI does not demand it back.

Comment thread http/structured_fields.ts
items: Item[];
/** Parameters associated with the inner list. */
parameters: Parameters;
parameters: ParameterMap;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small inconsistency that freezes with this PR: Dictionary and ParameterMap are ReadonlyMap, and the Dictionary JSDoc explicitly promises "returned dictionaries are immutable" — but InnerList.items is a plain Item[] and List is a mutable Array, so a parsed list can be mutated in place while a parsed dictionary can't.

Not worth blocking on, but if you want them consistent, now is the moment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b6957da. List is ReadonlyArray and InnerList.items is readonly Item[], with the same immutability note Dictionary has.

Comment thread http/structured_fields.ts
* @see {@link https://www.rfc-editor.org/rfc/rfc9651#section-3.1.2}
*/
export type Parameters = ReadonlyMap<string, BareItem>;
export type ParameterMap = ReadonlyMap<string, BareItem>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renaming away from the RFC's own term "Parameters" is the right call given the clash with TypeScript's built-in Parameters<T> — just flagging that the divergence becomes permanent here, so it's worth being a deliberate choice rather than an artifact of the rename.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deliberate, and now documented as such in the ParameterMap JSDoc (b6957da).

string,
token,
} from "./unstable_structured_fields.ts";
} from "./structured_fields.ts";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not blocking, but worth knowing what we're stabilizing on: the httpwg conformance suite is only partially vendored. key-generated.json, large-generated.json and the rest of the *-generated.json family are absent, as is the upstream serialisation-tests/ directory — so serialization is currently only exercised through canonical round-trips rather than against the suite's dedicated serialization cases.

The generated files are where the nastier boundary cases live (key length, list size, byte-sequence padding). Worth pulling in as a follow-up now that this is stable surface.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled in with 006d785 rather than waiting on a follow-up: the *-generated.json family, listlist.json (also missing), and serialisation-tests/ with a dedicated runner. You were right about the boundary cases. serializeDecimal rounded half away from zero instead of half to even, so 0.0025 serialized as "0.003". Fixed in the same commit.

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.03%. Comparing base (cdb83c6) to head (28f5616).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7232      +/-   ##
==========================================
- Coverage   95.03%   95.03%   -0.01%     
==========================================
  Files         618      618              
  Lines       51596    51853     +257     
  Branches     9340     9392      +52     
==========================================
+ Hits        49035    49279     +244     
- Misses       2021     2030       +9     
- Partials      540      544       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Adds the missing *-generated.json parsing vectors, listlist.json, and the
serialisation-tests/ directory, with a runner for serialisation-only cases.
Fixes serializeDecimal to round half to even per RFC 9651 4.1.5, which the
serialisation vectors exercise.
@tomas-zijdemans

Copy link
Copy Markdown
Contributor Author

Thanks for the review, all four notes addressed.

  • structured-fields is subpath-only now (9301b98). Dropped the mod.ts re-export and added the file to the check_mod_exports skip list.
  • List is ReadonlyArray and InnerList.items is readonly Item[], matching Dictionary (b6957da).
  • Vendored the rest of the httpwg suite in 006d785 rather than waiting on a follow-up: the *-generated.json family, listlist.json (also missing), and serialisation-tests/ with a dedicated runner. You called it on the boundary cases. The serialisation vectors caught that serializeDecimal used Math.round instead of round-half-even, so 0.0025 serialized as "0.003". Fixed in the same commit. 2109 conformance steps pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants