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

@alfiz/prisma

v0.8.1

Published

The Prisma storage driver for the Alfiz Application: implements the storage seam over any Prisma client generated from the bundled schema fragment, with @prisma/client kept out of the dependency graph via a structural delegate interface.

Readme

@alfiz/prisma

The Prisma storage driver for the Alfiz Application. It implements the storage seam (StorageDriver from @alfiz/application) over a Prisma client — and does it without depending on @prisma/client: the driver is written against a structural interface (AlfizPrismaDelegates) that any client generated from the bundled schema fragment satisfies.

1. Merge the schema fragment

Copy the models from prisma/schema.prisma into your application's own schema.prisma (they are a fragment — no datasource or generator blocks — and all models are prefixed Alfiz to avoid collisions), then migrate and generate as usual:

npx prisma migrate dev
npx prisma generate

Since 0.8.0 the fragment is v2: every model carries an app partition discriminator (@default("")) with composite keys led by it, so several Applications can share one set of tables. A single-application deployment changes nothing — an unpartitioned driver reads and writes partition "", which is where a migrated v1 dataset lands. Upgrading from the v1 fragment is a column-add plus PK/index rebuilds; see docs/MIGRATING.md §12 in the repository root.

2. Construct the driver

import { PrismaClient } from "@prisma/client";
import { createApplication } from "@alfiz/application";
import { prismaDriver } from "@alfiz/prisma";

const prisma = new PrismaClient();
const storage = prismaDriver(prisma); // structural match — no adapter, no cast
const app = createApplication({ storage /* ... */ });

That no-cast promise is pinned in CI by a compile-only fixture (src/prisma-client-shape.ts) replicating the exact input types prisma generate emits — Json inputs that reject bare null, bigint | number scalars, Prisma-style optional properties — so a delegate-surface change that would force as unknown as AlfizPrismaDelegates on adopters fails this package's own build instead. The match holds under exactOptionalPropertyTypes too.

The invalidation log (AlfizEpoch / AlfizEvent)

The fragment includes two models backing the Application's events: { persist: true } option — the persisted invalidation log that lets clients on OTHER processes revalidate their caches with one single-row read (AlfizEpoch) instead of waiting out a TTL. They are additive: merge them and prisma migrate dev as usual; the epoch row is lazily created on first append, no seed required. A client generated WITHOUT them still satisfies the driver interface — the driver then omits the optional event methods and events.persist refuses at construction.

Permission metrics (AlfizMetric)

One more additive model backs the Application's metrics: {} option: rolling counter buckets, keyed by (bucket, dimension, subject, metric) and incremented by upsert, so every app server reporting a window sums into the same numbers. Storage is bounded by attributed rows × retention ÷ bucket size, and compaction is a deleteMany past the retention cutoff. Like the log models, a client generated WITHOUT it still satisfies the driver interface — the driver omits the metric methods and the Application refuses metrics at construction rather than accepting batches that go nowhere. Nothing in this table is access data: dropping it loses counts and changes no decision.

Partitioned storage: several Applications, one set of tables

const docs = prismaDriver(prisma, { partition: "docs", lock });
const zoom = prismaDriver(prisma, { partition: "zoom", lock });

Each driver is pinned to its partition at construction and cannot address any other. The discriminator is unforgettable by construction: the delegate types require app on every where and create shape, so a query inside the driver that omitted the partition would fail to compile rather than scan every tenant — pinned by the same compile-only fixture as the no-cast promise. The recommended partition key is the application's primary catalog namespace, and the rule in shared tables is all partitioned or none: an application that omits the option lands in partition "" beside any legacy data.

Isolation here is logical and cooperative — every co-tenant holds credentials to the whole table set. When isolation must hold against a compromised co-tenant rather than against bugs, prefer schema-per-application via the connection string (?schema=docs on Postgres; a separate database on MySQL) with separate credentials: one PrismaClient per app, zero schema changes, isolation enforced by the database. Grading a shared-table deployment (or your own driver) is what isolationContractCases / meshContractCases in @alfiz/application/driver-suite exist for.

Multi-node deployments: pass an advisory lock

runExclusive defaults to an in-process mutex, which serializes graph writes within one process only. If several nodes share the database, supply a database advisory lock so two nodes cannot jointly write a graph cycle — and, with event persistence on, so two nodes cannot interleave sequence allocation in the invalidation log (event appends serialize under the same lock, key alfiz:events):

const storage = prismaDriver(prisma, {
  lock: (key, fn) =>
    prisma.$transaction(async (tx) => {
      await tx.$executeRaw`SELECT pg_advisory_xact_lock(hashtext(${key}))`;
      return fn();
    }),
});

The keys the driver hands to lock already carry the partition ("docs:groups" under partition: "docs"), so co-tenants sharing one database never contend on each other's advisory locks. Supplying lock is also what marks the driver cross-process capable (StorageDriver.crossProcess) — a prerequisite for accepting a mesh WRITE edge into the partition, where a peer Application is a second process executing this partition's semantics (openPeerApplication in write mode refuses without it, loudly).

What lives where

The driver stores and retrieves; it never interprets. All ids are opaque strings assigned by the Application layer, which also owns graph integrity, request workflows, catalog versioning, and the audit log. Epoch-ms timestamps are stored as BigInt columns for lossless round-tripping; optional core fields map to nullable columns (undefined ↔ NULL).