♻️ Provide url via context api - #1253
Merged
Merged
Conversation
The sitemap and every page's canonical link build absolute urls, and each
derived its origin by reaching for the request through
`useAbsoluteUrlFactory`, a helper parked in the current-request plugin.
There was no seam to change what a document advertises, and a plugin module
is middleware — it should not also be where url logic lives.
`www/context/url.ts` is an api built with `createApi`, like `fetch`,
`process` and `logger`. `url(path)` resolves one url and
`canonical({ base })` gives the production original, so `urlApi.around` can
rebase either, and because it resolves one path at a time an override sees
each path rather than a builder it would have to wrap.
The plugin module keeps only its plugin.
`around` takes a `Partial`, but it rebuilt every field of the api, so a field the caller left out was replaced by a call to `undefined` and threw `middleware is not a function` the next time anything used it. Nothing had hit it: `fetch` and `useProcess` are single-field apis, and every `logger` caller lists all four. A field the partial omits now keeps the middleware it already had. See #1252.
commit: |
taras
added this pull request to stack #1254
September 27, 2026 19:46
Merging this PR will improve performance by 10.13%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Memory | effection-inline.recursion |
5.4 KB | 4.9 KB | +10.13% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing tm/url-api (5d228a8) with v4 (e35d818)
Contributor
|
🚀 Deploy Preview Ready!
|
`canonical` took its base as an argument, so the one url that names production was a string literal inside a JSX layout component, and no deployment could say anything about it. `base` is a field on `UrlApi` now. `canonical()` reads it back through the api, `--canonical-base` supplies it, and `main` applies it with `urlApi.around` — the same way `initFetch` and `verboseLogging` configure theirs. A preview can name itself rather than pointing reviewers at production. Joining the base to the request path is normalized, because a base with no path of its own contributes a `/` that the request path then repeats. That could not happen while the base was always `https://frontside.com/effection`.
`www/context/context-api.ts` was a copy of `@effectionx/context-api`,
identical to the 0.2.1 published on JSR apart from the import specifier and
two stray type aliases. It had gone stale: npm has carried the package to
0.6.0 while the vendored copy sat where it was pasted.
0.6.0 fixes the `around` defect this branch was carrying a patch for — it
keeps a list of middleware per field rather than rebuilding the whole
record, so a partial simply never touches the fields it omits. The previous
commit's fix goes away with the file.
It also lifts a plain value into an `Operation`, so `base` is a `string` in
the api and the middleware that supplies it is an ordinary function:
base: () => options.canonicalBase
See #1252, which is fixed upstream.
cowboyd
approved these changes
Sep 27, 2026
This branch was successfully deployed
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.
Replaces #1250, which GitHub marked merged when a force-push briefly made its
head an ancestor of its base. Nothing had merged; the commits are the same.
Motivation
The sitemap and every page's canonical link build absolute urls, and each
derived its origin by reaching for the request through
useAbsoluteUrlFactory,a helper parked in the current-request plugin.
That left no seam. What a document advertises was fixed by whatever served it,
and the one url that names production — the canonical link — was a string
literal inside a JSX layout component, so no deployment could say anything about
it. A plugin module is middleware; it should not also be where url logic lives.
Approach
www/context/url.tsis an api built withcreateApi, alongsidefetch,processandlogger:urlresolves one path at a time rather than handing back a builder, somiddleware installed with
urlApi.aroundsees each path and can rebase somewhile leaving others alone.
canonical()readsbaseback through the api, soan override of the base reaches it too.
--basesupplies that base, andmainapplies it the same wayinitFetchandverboseLoggingconfigure theirs:It defaults to
https://frontside.com/effection, so nothing changes unless adeployment says otherwise — and now a preview can name itself instead of
pointing reviewers at production. The name matches staticalize's own
--base,which means the same thing.
The call sites that consumed the old builder synchronously move to
all(): thesitemap's entries and the canonical link.
plugins/current-request.tsis leftholding only its plugin.
The vendored context-api is gone
www/context/context-api.tswas a copy of@effectionx/context-api,identical to the 0.2.1 on JSR apart from the import specifier and two stray type
aliases. It had gone stale — npm has carried the package to 0.6.0 while the copy
sat where it was pasted. This depends on
npm:@effectionx/context-api@^0.6.0and deletes the file.
That matters beyond tidiness.
aroundtook aPartialbut rebuilt every fieldof the api, so a field the caller omitted became a call to
undefined(#1252).
UrlApiis the repo's first multi-field api, so it is the first placea partial override is the natural thing to write. 0.6.0 fixes it upstream by
keeping a list of middleware per field, so the patch this branch was carrying
goes away with the file it patched.
0.6.0 also lifts a plain value into an
Operation, which is whybaseis astringin the api and the middleware supplying it is an ordinary function.Note the registry: every other
@effectionx/*dependency here comes from JSR,but JSR is three minor versions behind for this one and still ships the broken
around.Tests
Mostly a refactor, so the check is that nothing it produces changed. I booted
the site on this branch and on
v4and diffed what it serves:The new capability works from either source:
llms.txtcorrectly keeps using the serving origin rather than the base — thetwo are different questions and stay different.
Joining the base to the request path is normalized, because a base with no path
of its own contributes a
/that the request path then repeats. That could nothappen while the base was always
https://frontside.com/effection, and it isexactly what a bare-domain preview url would hit.
The other two apis still work through the package:
--verbosetoggles thelogger middleware, and
/xrenders, which goes throughfetchand Octokit.deno fmt,deno lintanddeno checkare clean; the unit suite passes.Stack