Skip to content

Bump deps to latest, fix single-use servers and broken docs - #4

Merged
v1rtl merged 4 commits into
masterfrom
deps-and-fixes
Aug 7, 2026
Merged

v1rtl merged 4 commits into
masterfrom
deps-and-fixes

Conversation

@v1rtl

@v1rtl v1rtl commented Aug 7, 2026

Copy link
Copy Markdown
Member

Updates every dependency to its current release, then fixes the bugs that surfaced while verifying the update. Four commits, each self-contained.

1. 38f9d0c — bump deps

Dep Before After
@std/assert 1.0.0 1.0.19
@std/media-types 1.0.1 1.1.0
@std/testing 0.225.3 1.0.20
@std/expect 0.224.5 1.0.20
gql deno.land/x/gql@3.0.1 jsr:@deno-libs/gql@4.1.0

deno.land/x is deprecated, so the e2e test's gql import moves to JSR.

graphql stays on ^16.10.0 rather than 17.x: @deno-libs/gql pins npm:graphql@16.10.0 exactly, and a second copy of graphql in the module graph trips GraphQL.js' cross-realm instanceof checks (Cannot use GraphQLSchema from another module or realm). The lockfile confirms a single deduped copy.

Dependencies also move into a deno.json import map, which clears 6 pre-existing no-import-prefix lint errors and makes deno outdated usable. deno.lock is regenerated, dropping entries that were no longer reachable (@std/http, xss, commander, cssfilter, and the deno.land/x/gql remote hashes).

No behaviour change — tests stayed at 8 passed / 29 steps, identical to master.

2. 852f215 — fixes

All five bugs pre-date this PR; each was confirmed against unmodified master.

  • makeFetch was single-use. Both branches shut the server down after the first response, so a second call hit a dead port. The repo's own example.ts crashed on request 2 with ECONNREFUSED. Nothing caught it, because every test created a fresh makeFetch. A bare handler is now hosted per request, so an instance serves any number of requests and an unused one never leaves a server listening.
  • A failed request killed the process. The Deno.HttpServer branch wrapped the request in new Promise((resolve) => setTimeout(async () => ...)) with no reject path, so anything thrown inside escaped as an unhandled rejection instead of rejecting the returned promise — uncatchable by the caller. The setTimeout served no purpose and is gone.
  • URL arguments produced a malformed URL. FetchFunction accepts string | URL, but the URL was interpolated into a template literal, yielding http://localhost:41655http://example.com/x. Paths, relative URLs and absolute URLs now all resolve to the test server.
  • Six tests were vacuous. The try { assert } catch (e) { expect(...) } shape passes whenever nothing is thrown, so the assertions in the catch never ran. Replaced with expect().toThrow().
  • One test covered deleted code. should throw error if free port cannot be found asserted on an Unable to get free port path removed in 74a96e9, and permanently replaced globalThis.Deno.listen with a throwing stub. Deleted.

Also passes onListen: () => {} so the suite stops printing Listening on http://0.0.0.0:PORT/ once per request, and excludes .claude, .github, .vscode and tests from the published package — .claude/settings.local.json was being uploaded to JSR.

New coverage for request reuse, caller-owned server lifetime, URL objects, and query/hash preservation.

Important

Behaviour change: superfetch no longer calls shutdown() on a Deno.HttpServer you passed in — that server is yours. Callers must close it themselves, as they already had to for anything past the first request. Given the lifecycle change this is released as 4.0.0 (467bd7b) rather than 3.0.1. Note that publish.yml runs npx jsr publish on any push to master, so merging this publishes @deno-libs/superfetch@4.0.0.

3. 3544164 — docs

Every README code block was broken: makeFetch(s) against an undefined s instead of the handler declared above it, no closing }), and a trailing run() from the pre-Deno-2 test runner. None of it would execute if pasted.

Rewritten, plus documentation for the previously uncovered parts of the API: chained assertions, how expect() dispatches on its arguments, Content-Type-driven body decoding, RequestInit, reuse, and the caller-owned-server case. Also notes that the body is already consumed by the assertions, so res.json() cannot be called again.

The docs badge and link pointed at doc.deno.land/https/deno.land/x/superfetch; both now point at JSR. egg.json is deleted — nest.land is defunct and the manifest still claimed version 1.0.2.

Verification

Beyond a green suite, each fix was checked to actually bite:

  • New tests vs. old mod.ts — reverting mod.ts alone gives 5 failures plus a runner crash from the unhandled rejection, so the regression tests are real.
  • Mutation test — patching expectStatus to never throw now fails the suite; under the old try/catch it passed silently.
  • README — all 8 code blocks were extracted and executed against mod.ts, including the values in their trailing comments.
  • example.ts — runs clean, exit 0. It crashed before.
  • 5 consecutive runs — green, no port-collision flake from the per-request servers.

deno fmt / lint / check clean · 10 passed / 33 steps · deno publish --dry-run down to 9 files.

🤖 Generated with Claude Code

v1rtl and others added 4 commits August 7, 2026 13:20
Update all JSR dependencies to their current releases:

  @std/assert       1.0.0    -> 1.0.19
  @std/media-types  1.0.1    -> 1.1.0
  @std/testing      0.225.3  -> 1.0.20
  @std/expect       0.224.5  -> 1.0.20

Replace the deno.land/x/gql@3.0.1 import in the e2e test with
jsr:@deno-libs/gql@4.1.0 — deno.land/x is deprecated and frozen.

graphql stays on ^16.10.0 rather than 17.x: @deno-libs/gql pins
npm:graphql@16.10.0 exactly, and a second copy of graphql in the
module graph trips GraphQL.js' cross-realm instanceof checks
("Cannot use GraphQLSchema from another module or realm"). The
lockfile confirms a single deduped graphql.

Move every dependency into a deno.json import map and reference them
by bare specifier. This clears the 6 pre-existing `no-import-prefix`
lint errors and makes `deno outdated` / `deno update` usable for
future bumps.

Regenerate deno.lock, dropping entries that were no longer reachable
(@std/http, xss, commander, cssfilter, and the deno.land/x/gql
remote hashes).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
makeFetch was unusable for more than one request. Both branches shut the
server down after the first response, so a second call hit a dead port --
the repo's own example.ts crashed on request 2 with ECONNREFUSED. Covered
by nothing, because every test created a fresh makeFetch.

The Deno.HttpServer branch wrapped the request in
`new Promise((resolve) => setTimeout(async () => ...))` with no reject
path, so anything thrown inside escaped as an unhandled rejection and
tore the process down instead of rejecting the returned promise -- an
uncatchable failure for callers. Dropping the pointless setTimeout and
using a plain async function fixes it.

`FetchFunction` accepts `string | URL`, but the URL was interpolated into
a template literal, yielding `http://localhost:41655http://example.com/x`
and a TypeError. Resolve properly and force the origin to the test server
so a path, a relative URL and an absolute URL all reach the handler.

Changes:

- Host a bare handler per request instead of once per makeFetch(). An
  instance now serves any number of requests, and one that is never
  called no longer leaves a server listening.
- Stop shutting down a caller-supplied Deno.HttpServer. We do not own it.
  BEHAVIOUR CHANGE: callers passing their own server must now close it
  themselves, as they already had to for anything past the first request.
- Pass `onListen: () => {}` so the suite stops printing "Listening on
  http://0.0.0.0:PORT/" once per request.

Tests:

- Replace six `try { assert } catch (e) { expect(...) }` blocks with
  expect().toThrow(). The old shape passed whenever nothing was thrown,
  so the assertions in the catch never ran; a mutation that makes
  expectStatus stop throwing now fails the suite instead of passing.
- Delete 'should throw error if free port cannot be found'. It asserted
  on an "Unable to get free port" path removed in 74a96e9, and it
  permanently replaced globalThis.Deno.listen with a throwing stub.
- Add coverage for request reuse, caller-owned server lifetime, URL
  objects and query/hash preservation.

Also exclude .claude, .github, .vscode, egg.json and tests from the
published package -- .claude/settings.local.json was being uploaded to
JSR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every code block in the README was broken. Both examples called
makeFetch(s) against an undefined `s` instead of the `handler` declared
a line above, neither closed its describe() block, and both ended with a
bare run() left over from the pre-Deno-2 test runner -- none of it would
execute if pasted.

Rewrite the examples so they run as written, and cover the parts of the
API that had no documentation at all: chained assertions, how expect()
dispatches on its arguments, Content-Type-driven body decoding, passing
RequestInit, reusing a fetch across requests, and testing an existing
Deno.HttpServer (including that the caller now shuts it down). Note that
the response body is already consumed by the assertions.

Every block was extracted and executed against mod.ts to confirm it
passes, including the values in the trailing comments.

Point the docs badge and link at JSR -- they referenced
doc.deno.land/https/deno.land/x/superfetch, which is the deprecated
host. Add the missing install instruction.

Delete egg.json. nest.land is defunct, and the manifest still described
the package as version 1.0.2 with a stale test command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
makeFetch no longer shuts down a Deno.HttpServer supplied by the caller,
so callers passing their own server must now close it themselves. That
is a breaking change to the lifecycle contract, hence a major bump
rather than a patch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@v1rtl
v1rtl merged commit fe290af into master Aug 7, 2026
2 checks passed
@v1rtl
v1rtl deleted the deps-and-fixes branch August 7, 2026 10:30
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.

1 participant