Skip to content

fix: apply the encode callback to empty cookie values#277

Merged
blakeembrey merged 3 commits into
jshttp:masterfrom
spokodev:fix/encode-empty-value
Jun 24, 2026
Merged

fix: apply the encode callback to empty cookie values#277
blakeembrey merged 3 commits into
jshttp:masterfrom
spokodev:fix/encode-empty-value

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

The documented encode option is meant to mirror decode and apply to every value, so that a custom encoder (signing, encrypting, base64, etc.) round-trips correctly with its matching decode.

stringifySetCookie instead used a truthiness test:

const value = rawValue ? enc(rawValue) : "";

For an empty-string value this skips the encoder entirely and emits a raw name=, instead of name=<encode("")>. That silently breaks encode/decode symmetry whenever the value happens to be empty.

The sibling stringifyCookie helper always calls the encoder, and so did cookie@0.7.2, so this is also an inconsistency between the two stringify paths.

The fix swaps the truthiness check for a null check:

const value = rawValue != null ? enc(rawValue) : "";

Empty strings are now routed through the encoder, while the object-form value: undefined still maps to an empty value, preserving existing behavior.

Added a test that fails before the change and passes after:

it("should apply the encoder to an empty value", function () {
  expect(
    cookie.stringifySetCookie("foo", "", { encode: (v) => "[" + v + "]" }),
  ).toEqual("foo=[]");
});

Full suite stays green (230 tests).

The encode option is documented to mirror decode and should run for
every value, but stringifySetCookie used a truthiness test that skipped
it for the empty string, emitting a raw name= instead of the encoded
result. This broke encode/decode symmetry for signing and encrypting
encoders.

Use a null check so empty strings are routed through the encoder while
the object-form value: undefined still maps to an empty value. This
matches the sibling stringifyCookie helper and cookie@0.7.2, which both
always call the encoder.
@blakeembrey

Copy link
Copy Markdown
Member

Since the documentation states it should be a string, I think this should always just be enc(rawValue). However, I don't think it's worth changing except in a major version. I can't recall why I made it skip encode, it was probably a premature performance optimization.

@spokodev

Copy link
Copy Markdown
Contributor Author

Agreed that always enc(rawValue) is the cleaner contract. My change is narrower and non-breaking: it only routes the empty-string value through encode like every other value, so a custom encode sees consistent behavior without a major bump. If you would rather fold this into the broader enc(rawValue) change in the next major, I am fine closing this in favor of that. Your call on timing.

@blakeembrey

Copy link
Copy Markdown
Member

I'd just fold it into a new major, that's fine by me. No one has mentioned it as an issue and explicitly unsetting a cookie is usually done by passing an empty string as the value, so making it potentially longer isn't much of a plus 🤷

@spokodev

Copy link
Copy Markdown
Contributor Author

Fair enough - the empty value is usually an unset, so leaving it unencoded is the lighter default. I will close this in favor of folding it into the next major. Thanks for thinking it through.

@spokodev spokodev closed this Jun 24, 2026
@blakeembrey

Copy link
Copy Markdown
Member

Would you like to update this PR or make another? A new major will occur this or next week.

@blakeembrey blakeembrey reopened this Jun 24, 2026
@blakeembrey

Copy link
Copy Markdown
Member

😅 It looks like I didn't realize my types do allow undefined, so LGTM, I merged with master and it'll be good to land for the next major.

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (4898ba2) to head (099ed03).
⚠️ Report is 35 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master      #277   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines          160       193   +33     
  Branches        69        82   +13     
=========================================
+ Hits           160       193   +33     

☔ 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.

@blakeembrey blakeembrey merged commit 581e9df into jshttp:master Jun 24, 2026
9 checks passed
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