Skip to content

Read a URL encoded CSV in the #csv= fragment - #52

Merged
aaronj1335 merged 1 commit into
mainfrom
claude/csv-fragment-url-encoding-7q3xnw
Sep 16, 2026
Merged

aaronj1335 merged 1 commit into
mainfrom
claude/csv-fragment-url-encoding-7q3xnw

Conversation

@aaronj1335

Copy link
Copy Markdown
Owner

#csv= took only base64url bytes, so the small hand-written link -- the
one ?csv= already takes -- had to go through a gzip pipeline before it
could stay out of the request. It now takes percent-encoded CSV text too.

Which encoding a link uses is read off the payload rather than declared:
base64url spells everything in A-Z a-z 0-9 - _ =, and a CSV needs the
comma between its date column and a value column, so a payload holding a
character outside that alphabet is text and never a mangled base64url
string. A base64url payload run through encodeURIComponent arrives with
its padding as %3D, which is why the percent decoding happens first.

URLSearchParams no longer reads the fragment. It decodes what it
returns, which would decode a percent-encoded CSV here and again in
decodeCSVFragment -- a cell holding the literal %0A would come back as
a row break -- and it form-decodes, reading a + in a cell as a space.
The raw value is handed over instead, and decoded exactly once.

The percent decoding works escape by escape rather than over the whole
string, because decodeURIComponent is all-or-nothing: one cell holding a
bare % would otherwise cost the newlines in every row.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_017hKewm4Qv8pVHVYDfBYbHg

`#csv=` took only base64url bytes, so the small hand-written link -- the
one `?csv=` already takes -- had to go through a gzip pipeline before it
could stay out of the request. It now takes percent-encoded CSV text too.

Which encoding a link uses is read off the payload rather than declared:
base64url spells everything in `A-Z a-z 0-9 - _ =`, and a CSV needs the
comma between its date column and a value column, so a payload holding a
character outside that alphabet is text and never a mangled base64url
string. A base64url payload run through `encodeURIComponent` arrives with
its padding as `%3D`, which is why the percent decoding happens first.

`URLSearchParams` no longer reads the fragment. It decodes what it
returns, which would decode a percent-encoded CSV here and again in
`decodeCSVFragment` -- a cell holding the literal `%0A` would come back as
a row break -- and it form-decodes, reading a `+` in a cell as a space.
The raw value is handed over instead, and decoded exactly once.

The percent decoding works escape by escape rather than over the whole
string, because `decodeURIComponent` is all-or-nothing: one cell holding a
bare `%` would otherwise cost the newlines in every row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017hKewm4Qv8pVHVYDfBYbHg
@aaronj1335
aaronj1335 force-pushed the claude/csv-fragment-url-encoding-7q3xnw branch from 3727d3e to 1c9b46b Compare September 16, 2026 11:46
@aaronj1335
aaronj1335 merged commit f29b536 into main Sep 16, 2026
5 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