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

@loewen-digital/fullstack

v0.3.0

Published

Backend primitives for any JavaScript meta-framework

Readme

@loewen-digital/fullstack

Backend primitives for JavaScript meta-frameworks: auth, sessions, validation, mail, storage, cache, queue, events, logging, errors, permissions, notifications, i18n, search, webhooks, realtime, and a Drizzle db module for apps on a SQL database. One package with one subpath per module, factory functions instead of a container, swappable drivers, Web Standard Request and Response everywhere, a framework-agnostic core and adapters for SvelteKit, Nuxt, Remix and Astro.

Documentation: fullstack-docs-vitepress.pages.dev

Install

npm install @loewen-digital/fullstack

Node 24, ESM only, TypeScript 5. The modules talk HTTP through fetch or take a client you hand them, so you install only what your drivers need: @loewen-digital/flatdb and zod for auth on flatdb, nodemailer for SMTP, drizzle-orm and better-sqlite3 for the db module (better-sqlite3 also for the sqlite search driver), a Redis client for the redis drivers. The package itself has no dependencies. Details on the installation page.

What it looks like

Every module is a factory that takes a typed config and returns an instance; nothing is global. auth stores nothing itself: it takes an AuthDbAdapter, and @loewen-digital/fullstack/auth/flatdb ships one for flatdb collections.

// src/lib/server/stack.ts
import { createAuth, type AuthDbAdapter } from '@loewen-digital/fullstack/auth'
import { createSession } from '@loewen-digital/fullstack/session'
import { createMail } from '@loewen-digital/fullstack/mail'

export declare const authDb: AuthDbAdapter // createFlatdbAuthAdapter({ users, sessions, tokens }), or your own

export const auth = createAuth({ sessionTtl: 7 * 24 * 3600 }, { db: authDb })
export const session = createSession({ driver: 'cookie', secret: process.env.SESSION_SECRET! })
export const mail = createMail({ driver: 'console', from: 'My App <[email protected]>' })

The adapter wires them into the framework. On every request it opens the session cookie, validates the auth cookie and checks the CSRF header on mutating requests; the result lands on event.locals.

// src/hooks.server.ts
import { createHandle } from '@loewen-digital/fullstack/adapters/sveltekit'
import { auth, session } from '$lib/server/stack'

export const handle = createHandle({ auth, session })

Validation is one async function with pipe-string rules:

import { validate } from '@loewen-digital/fullstack/validation'

declare const input: Record<string, unknown>

const result = await validate(input, { email: 'required|email', password: 'required|string|min:8' })
if (!result.ok) console.log(result.errors) // [{ field: 'password', rule: 'min', message: '...' }]

The quick start builds register, login, a guarded page and logout from these pieces. The code blocks in this file and in the docs are type-checked against the package by the test suite.

Modules

All subpaths sit below @loewen-digital/fullstack. Each module's page lists its API, the options its factory reads and their defaults.

| Module | Subpath | What you get | |---|---|---| | Config | /config | defineConfig, loadConfig, env() with typed fallbacks; createStack(config, { authDb }) from the root builds every configured module | | Validation | /validation | validate(data, rules) with pipe-string or object rules, defineRules for your own; no I/O | | Auth | /auth, /auth/flatdb | createAuth(config, { db }): password hashing, sessions, email verification, password reset, one-time tokens, OAuth; createFlatdbAuthAdapter for flatdb collections | | DB | /db | createDb(config, schema): Drizzle on SQLite with migrations, seeds, factories and pagination, for apps on a SQL database, Node-only; needs drizzle-orm and better-sqlite3. Apps on flatdb call flatdb directly | | Session | /session | createSession: flash messages and old input on memory, cookie (signed, stateless) or redis | | Security | /security | createSecurity: CSRF tokens, CORS headers, a rate limiter in memory or on the Cloudflare Rate Limiting binding, sanitize | | Mail | /mail | createMail: console, SMTP via nodemailer, Resend, Postmark; {{ }} templates | | Storage | /storage | createStorage: memory, local, S3, R2 (S3 API), the R2 bucket binding | | Cache | /cache | createCache: memory, Redis, Cloudflare KV; remember(key, ttl, fn) | | Queue | /queue | createQueue: memory, Redis, Cloudflare Queues; retries and a dead-letter list | | Events | /events | createEventBus, defineEvents: a typed in-process bus | | Logging | /logging | createLogger: console, file and external transports, child loggers | | Errors | /errors | FullstackError and the HTTP errors, errorToResponse, isFullstackError | | Permissions | /permissions | createPermissions: roles with wildcards and inheritance, policies, can and authorize | | Notifications | /notifications | createNotifications: one notify call fans out to mail, in-app, SMS and push | | i18n | /i18n | createI18n: t, tn for plurals, number, date, loadTranslations | | Search | /search | createSearch: SQLite FTS5 via better-sqlite3, Meilisearch, Typesense | | Webhooks | /webhooks | createWebhooks: verify signed incoming webhooks, send outgoing ones with retries and a delivery log | | Realtime | /realtime | createRealtime: channels with broadcast, sse() as a streaming Response | | Testing | /testing | createTestStack, fake mail, queue and storage drivers, defineFactory, withSavepoint |

A driver named in the config (memory, console, cookie, sqlite) needs nothing else. A driver that needs credentials or a client is built with its own factory and handed to the module's createXInstance, for example createMailInstance(createResendDriver({ apiKey }), { driver: 'resend', from }). The driver pattern page lists every driver and which kind it is.

Adapters

The core imports nothing from a framework; the adapters do the wiring. Each opens the session cookie, validates the auth cookie, checks the CSRF header on mutating requests and offers helpers for login, logout and form validation.

| Framework | Subpath | Entry point | |---|---|---| | SvelteKit | /adapters/sveltekit | createHandle(stack) fills event.locals; validateForm, setAuthCookie, clearAuthCookie, getCsrfToken | | Nuxt | /adapters/nuxt | createNuxtMiddleware(stack) fills event.context | | Remix | /adapters/remix | createRemixLoader(stack) and createRemixAction(stack) wrap loaders and actions | | Astro | /adapters/astro | createAstroMiddleware(stack) fills Astro.locals |

Testing

createTestStack() returns mail, storage and queue on fake drivers that record what happened, and cache and session on memory drivers. Pass db: { driver: 'sqlite', url: ':memory:' } to add a Drizzle db for a SQL app.

import { createTestStack } from '@loewen-digital/fullstack/testing'

const stack = createTestStack()
await stack.mail.send({ to: '[email protected]', subject: 'Welcome', text: 'Hi' })
stack.fakeMail.sentTo('[email protected]') // one message
stack.reset()

defineFactory, sequence and pick build test records; withSavepoint, createDbCleaner and seedOnce keep SQLite tests isolated. See the testing pages.

CLI

The package installs a fullstack binary that reads fullstack.config.ts from the current directory and needs its db section.

npx fullstack migrate                      # pending Drizzle migrations from db.migrations
npx fullstack migrate:rollback
npx fullstack migrate:status
npx fullstack seed [file]                  # database/seeds/index.ts by default
npx fullstack generate migration <name>    # also: generate factory <name>, generate seed <name>

Vite plugin and Dev UI

fullstackPlugin loads fullstack.config.ts, exposes it as virtual:fullstack/config and writes fullstack.d.ts for it. During vite dev it serves the Dev UI at /__fullstack/: mail sent through the console driver, jobs on the memory queue, log lines, cache entries and the config.

// vite.config.ts
import { defineConfig } from 'vite'
import { fullstackPlugin } from '@loewen-digital/fullstack/vite'

export default defineConfig({ plugins: [fullstackPlugin()] })

Requirements

  • Node 24 (engines.node), or Bun, Deno and Cloudflare Workers with nodejs_compat for the modules that need no Node API (db, local storage, the file log transport and SMTP do)
  • ESM only; there is no CommonJS build
  • TypeScript 5 with moduleResolution set to bundler, node16 or nodenext

Contributing

The rules for changes live in AGENTS.md; every change a user would notice gets a line in CHANGELOG.md. npm run lint && npm run typecheck && npm test && npm run build must pass.

License

MIT