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

@letters-patent/nuxt

v0.0.1

Published

Nuxt module for letters-patent with SSR. It provides an `$auth` service resolved on the server and hydrated on the client, the `useAuth()` and `useUser()` composables, and the `defineAuthHandlers` server helper — all typed by the app's own contract.

Readme

@letters-patent/nuxt

Nuxt module for letters-patent with SSR. It provides an $auth service resolved on the server and hydrated on the client, the useAuth() and useUser() composables, and the defineAuthHandlers server helper — all typed by the app's own contract.

Setup

Declare your contract once and add the module:

// shared/contract.ts
import { defineLettersPatentConfig } from "letters-patent/config";

export const contract = defineLettersPatentConfig({
  scopes: ["docs:read", "docs:write"],
  roles: ["admin", "editor"],
  meta: { plan: ["free", "pro"] },
});

// nuxt.config.ts
import { contract } from "./shared/contract";

export default defineNuxtConfig({
  modules: ["@letters-patent/nuxt"],
  lettersPatent: { contract },
});

Write your provider (a set of callbacks over letters-patent's own domain objects — the h3 event is its own domain, closed over per request), then hand it to defineAuthHandlers in one server route file:

// server/api/auth/[action].ts
import { defineSchema } from "letters-patent";
import { contract } from "../../../shared/contract";
import { createMyProvider } from "../../providers/auth";

const schema = defineSchema(contract);

export default defineAuthHandlers(schema, (event) =>
  createMyProvider(schema, { event }, (claims) => ({ ... })),
);

That's the whole wiring. The browser-side transport that calls these routes ships with the module.

Usage

<script setup lang="ts">
const auth = useAuth();
const user = useUser(); // reactive User | null, shorthand for auth.current
</script>

<template>
  <p v-if="auth.authenticated">Hi, {{ user?.id }}</p>
  <button v-if="auth.can('docs:write')">Edit</button>
</template>

can/is carry the contract's vocabulary in their types — an undeclared scope fails to compile in the app.

Protecting pages

The module registers a named auth route middleware. Opt in per page, and declare the page's requirements — scopes must all hold, any listed role suffices — in the same vocabulary:

<script setup lang="ts">
definePageMeta({
  middleware: "auth",
  auth: { scopes: ["docs:write"], roles: ["editor"] },
});
</script>

An unauthenticated visitor is redirected to the login route (the login option, /login by default) with the attempted path in the redirect query parameter — /login?redirect=/editor. An authenticated user that falls short of the requirements gets a 403 error page, and the service's denied event fires with the failed check. The auth page-meta entry is typed by the contract, so an undeclared scope or role fails to compile.

Options

| Option | Default | Description | | ---------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | contract | — (required) | The app's contract: scopes, roles, and meta. | | prefix | "/api/auth" | The route prefix the auth endpoints live under. The handler file must sit at the matching Nitro path (server/api/auth/[action].ts for the default). | | login | "/login" | The route the auth middleware sends unauthenticated visitors to, with the attempted path as a redirect query parameter. |

How it works

  • At build time the module proves the configured contract, writes it and the route prefix to the #build/letters-patent.mjs template, and derives the AppContract literal type — so both sides of the wire share one vocabulary and the app surface is fully typed.
  • The runtime plugin derives the schema from the build contract, builds the service over a useState container and, on the server only, resolves the user so state populates before render and serializes to the client — no refetch or auth flash on hydration.
  • The module's transport is itself a provider whose vendor is the app's own auth routes: every callback dials ${prefix}/*, and each answer is proven against the schema before it lands in state.
  • The user's provider runs only inside the server handlers, constructed per request with the h3 event; the auth host never reaches the browser.