npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@erikt/framework-deno

v0.0.1

Published

Runs a `fetch` handler on [`Deno.serve`][serve], and implements `framework`'s `FileStore` port against Deno's filesystem. Along with `@erikt/framework-node` and `packages/tests`, this is one of the few places in the repo that may reach for a runtime-speci

Readme

@erikt/framework-deno

Runs a fetch handler on Deno.serve, and implements framework's FileStore port against Deno's filesystem. Along with @erikt/framework-node and packages/tests, this is one of the few places in the repo that may reach for a runtime-specific API.

It is the second worked example of the two adapter ports; the procedure for writing another is in ADAPTERS.md.

import { createApp, defineRoute } from '@erikt/framework'
import { serve } from '@erikt/framework-deno'

const app = createApp()

app.get('/', defineRoute(() => 'hello'))

const server = await serve(app, { port: 3000 })

console.log(`listening on ${server.url}`)

await server.close()
deno run --allow-net main.ts

serve(handler, options?)

handler is anything with a fetch(request): Response | Promise<Response> method — this framework's app, or any other WinterTC-style handler. The adapter does not depend on framework.

| Option | Default | | | --- | --- | --- | | port | 3000 | 0 binds an ephemeral port; read the real one from the handle | | hostname | 'localhost' | |

Resolves once the server is listening, with a handle:

| | | | --- | --- | | url | e.g. http://localhost:3000 | | hostname, port | The bound address — port is resolved, not the requested 0 | | close() | Shuts the server down, including idle keep-alive connections |

There is no request/response bridge here, because there is nothing to bridge: Deno.serve hands the handler a Request and takes its Response, so the URL, duplicate headers, streamed bodies in both directions, set-cookie splitting and client-disconnect aborts are all Deno's own doing. What the adapter adds is the handle, the start / stop calls, and the 500.

Two Deno.serve options are supplied rather than left to their defaults, and both are deliberate: onListen because Deno otherwise announces the port on stdout, and onError because Deno otherwise prints the stack trace. A throwing handler gets the client a plain 500, and nothing about the error.

hostname is reported back as it was given. Deno resolves localhost to ::1 and reports that on server.addr, which would make for a surprising banner.

denoStore(dir, options?)

The filesystem implementation of framework's FileStore: walk dir, import a module from it, read a file out of it.

import { createApp, fileRouter } from '@erikt/framework'
import { denoStore, serve } from '@erikt/framework-deno'

const app = createApp()
const store = denoStore(new URL('./', import.meta.url))

app.plugin(fileRouter({ store, dir: 'routes' }))

await serve(app, { port: 3000 })
deno run --allow-net --allow-read main.ts

One store per app; each feature scopes itself with its own dir, so the same store can back routes, layouts and assets.

dir is a file: URL or a path resolved against Deno.cwd(). A URL built from import.meta.url is the portable form — it does not depend on where the process was started, and it is the only form that needs no read permission on the working directory.

| Option | Default | | | --- | --- | --- | | name | 'deno' | Identifies the store in error messages |

| Method | | | --- | --- | | list({ prefix, extensions }) | Every file under dir, recursively, as a /-separated relative path. No stat per file, so no size or modified — those come from read | | read(path) | A Response streaming the file, with content-length and last-modified. null if the file is not there | | import(path) | The module, imported through its file: URL — which is what lets a [id].ts filename load at all |

Both read and import refuse a path that would leave dir, so a traversal attempt is an error rather than a file read.

Unlike nodeStore, read does not buffer: the body is Deno.open's readable, so a large asset is streamed to the client. The handle closes when the stream is read to the end or cancelled — a Response that is never consumed holds it open until collection. Neither store sets content-type; there is no MIME table in the repo yet.

Deno APIs used

Deno.serve for the server. Deno.readDir, Deno.open, Deno.cwd and Deno.errors.NotFound for the store. That is the whole runtime-specific surface, and there are no node:* imports — Deno supports them, but a web API exists for everything else the adapter does.

Two consequences of the zero-dependency rule are worth knowing:

  • Deno is declared locally, in the two modules that use it, rather than pulled in as a types package. @types/deno is unofficial and stale, and the repo forbids the dependency either way. The declarations cover only what is used, so deno check src/index.ts — which typechecks against Deno's own definitions — is the honest verification, and it passes.
  • Paths become file: URLs by hand, because node:url's pathToFileURL is out. escapePath percent-encodes only the characters the URL parser would otherwise act on — \ becomes a separator, ? and # truncate the path, and tab, newline and carriage return are dropped outright. A drive letter's : survives, which a general-purpose encoder would not have left alone.

Permissions

| | | | --- | --- | | serve | --allow-net | | denoStore | --allow-read — including the dynamic import() behind store.import, which is a runtime read |

Narrow them if you like: --allow-net=localhost:3000 and --allow-read=./src are enough for the example above.

Limits

Plain-TCP HTTP/1.1, as Deno.serve provides it. TLS — and so the HTTP/2 that Deno negotiates over it — and WebSocket upgrade are not exposed here, though Deno supports all three. Add them to this package if you need them; they do not belong in framework.

Tests

In packages/tests/framework-deno/, running the same conformance suite as @erikt/framework-node. The harness is still node --test: the Node test spawns a real Deno process — server.ts and bare.ts for the HTTP suite, report.ts for the store — and asserts over the wire and over the JSON it prints. Tests skip themselves where deno is not on PATH.