Repository navigation
feat: runtime API base URL and an SSE parser - #18
Merged
Merged
Conversation
Where the client sends API calls was `apiUrl` in environment.ts, a
build-time constant that has always been empty, so every call fell
through to `${BASE_URL}api/`. Changing it meant a rebuild, and the
deployment behind /exp needs the API at /exp/api, which the app's
own base path cannot express.
It is now `api.baseUrl` in config.json, used as written, so a
deployment sets it by mounting a config file. The default `/api` keeps
the same-origin behaviour of the root build. The base is read per call
and the axios instance built on first use, because configuration only
exists after loadRuntimeConfig() resolves and this module is imported
by tests.
Verified: lint, typecheck, knip, tests and build pass; the dev server
serves the new block in /config.json.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Experience API streams each answer as server-sent events over a POST, which the browser's EventSource cannot make. The client already reads the body from fetch; what it lacked was framing. Today it treats every network chunk as answer text. readSseEvents() turns a body into event/data frames by the SSE rules: blank line ends a frame, data lines join, comment lines and unknown fields are ignored, all three line endings work. Lines and multi-byte characters are reassembled across chunk boundaries, and a final frame the stream closes on is still delivered. Stopping early cancels the body so a live connection is closed. Nothing uses it yet; the next change does. Verified against the running API as well as the tests: its stream parses to started, two deltas and completed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The reference deployment runs the shipped config.json as it is, with nothing mounted over it, so the bundled default has to be the real value. The UI is served at /experience and the API at /experience/api behind one front proxy; `/api` pointed at the domain root and would have missed. The value is still used as written and not derived from the build's base path. The two can differ, and an API call that quietly went to the wrong place would be hard to trace. A deployment at another path changes this one key. The README's sub-path section now shows the proxy block the API path needs: buffering off so the answer streams, a read timeout that covers a whole turn, and a body limit above the DSS's own so the DSS is the one that says a conversation is too long. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
Ground work for talking to the Experience API. No visible change with stubs on, which is still the default.
api.baseUrlinconfig.jsonreplaces the empty, build-timeapiUrlconstant. Default/api, used as written. A deployment behind a sub-path sets it in its mounted config, for example/experience/api. The base is read per call so tests can import the module before config loads.src/lib/sse.ts. The API streams answers as server-sent events over a POST, whichEventSourcecannot make, so the body is read fromfetchand framed here. Handles comment lines, all three line endings, multi-line data, frames and multi-byte characters cut across chunks, and a final frame with no blank line after. Nothing uses it yet; PR feat: run the client with no backend via a stub API layer #2 does.Why
The client's server coupling has to move from the old backend's shape to the Experience API contract (
docs/implementation/experience-api-contract.md, local for now). Both pieces here are needed by that change and stand on their own.Verified
npm run lint,typecheck,knip,test(33 tests) andbuildpass at each commit. Route tree unchanged.npm run devserves the new block in/config.json.started, twodeltas andcompleted.Follow-ups noted from review
api.baseUrlmust be set for a sub-path deployment.🤖 Generated with Claude Code