Skip to content

♻️ Provide url via context api - #1253

Merged
taras merged 4 commits into
v4from
tm/url-api
Sep 27, 2026
Merged

taras merged 4 commits into
v4from
tm/url-api

Conversation

@taras

@taras taras commented Sep 27, 2026 •

Copy link
Copy Markdown
Member

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.ts is an api built with createApi, alongside fetch,
process and logger:

export interface UrlApi {
  base: string;
  url(path: string): Operation<string>;
  canonical(): Operation<string>;
}

export const { base, canonical, url } = urlApi.operations;

url resolves one path at a time rather than handing back a builder, so
middleware installed with urlApi.around sees each path and can rebase some
while leaving others alone. canonical() reads base back through the api, so
an override of the base reaches it too.

--base supplies that base, and main applies it the same way initFetch and
verboseLogging configure theirs:

yield* urlApi.around({
  base: () => options.base,
});

It defaults to https://frontside.com/effection, so nothing changes unless a
deployment 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(): the
sitemap's entries and the canonical link. plugins/current-request.ts is left
holding only its plugin.

The vendored context-api is gone

www/context/context-api.ts was 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.0
and deletes the file.

That matters beyond tidiness. around took a Partial but rebuilt every field
of the api, so a field the caller omitted became a call to undefined
(#1252). UrlApi is the repo's first multi-field api, so it is the first place
a 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 why base is a
string in 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 v4 and diffed what it serves:

IDENTICAL  sitemap.xml
IDENTICAL  canonical links on /, /blog, /guides/v4/scope

The new capability works from either source:

--base https://preview.example      →  https://preview.example/blog
BASE=https://env.example/docs       →  https://env.example/docs/blog

llms.txt correctly keeps using the serving origin rather than the base — the
two 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 not
happen while the base was always https://frontside.com/effection, and it is
exactly what a bare-domain preview url would hit.

The other two apis still work through the package: --verbose toggles the
logger middleware, and /x renders, which goes through fetch and Octokit.
deno fmt, deno lint and deno check are clean; the unit suite passes.

Stack

        v4
        tm/url-api            ← this
#1248   tm/staticalize-base
#1247   tm/markdown-routes

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.
@pkg-pr-new

pkg-pr-new Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/effection@1253

commit: 5d228a8

@taras
taras added this pull request to stack #1254 September 27, 2026 19:46
@codspeed

codspeed Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 10.13%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 5 untouched benchmarks

Performance Changes

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)

Open in CodSpeed

@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
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.
@taras taras changed the title ♻️ Make the url a site reads itself as a context api ♻️ Provide url via context api Sep 27, 2026
@taras
taras merged commit 61a2219 into v4 Sep 27, 2026
19 checks passed
@taras
taras deleted the tm/url-api branch September 27, 2026 21:50

This branch was successfully deployed

1 active deployment
Preview — 5d228a8f Deployed Sep 27, 2026 by taras via deploy-preview #1400
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