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

@glydi/passkey-store-prisma

v0.1.0

Published

First-party Prisma/Postgres adapter for Glide passkeys. Schema exposed as data — consumers paste the .prisma fragment and run their own prisma migrate.

Readme

@glydi/passkey-store-prisma

First-party Prisma/Postgres adapter for Glide passkeys. Provides a conformant GlideStore backed by two PostgreSQL tables — schema exposed as data via a .prisma fragment, zero numbered .sql files.

Quick Start

import { PrismaClient } from "@prisma/client";
import { createPrismaStore } from "@glydi/passkey-store-prisma";
import { createGlideServerFromEnv } from "@glydi/passkey-server";

const prisma = new PrismaClient();
const store = createPrismaStore({
  prisma,
  usersTable: "users",
  userIdColumn: "id",
  usernameColumn: "email",
});
const glide = createGlideServerFromEnv({ store });

All four opts are required — the adapter validates them at startup with clear [Glide] errors.

createGlideServerFromEnv defaults: userVerification defaults to "preferred". Set GLIDE_USER_VERIFICATION=required in your environment for stricter behavior (recommended for production). GLIDE_RP_NAME is required — set it to your app's display name (e.g. "My App").

Consumer-Owns-Migrations

Paste the following models from PRISMA_SCHEMA (exported from @glydi/passkey-store-prisma/schema) into your own schema.prisma, alongside your User model:

model PasskeyCredential {
  credentialId String  @id @map("credential_id")
  userId       String  @map("user_id")
  publicKey    Bytes   @map("public_key")
  counter      BigInt  @default(0)
  transports   String?
  deviceType   String? @map("device_type")
  backedUp     Int?    @map("backed_up")
  name         String?
  createdAt    BigInt? @map("created_at")

  // Replace "User" with your actual user model name.
  // Add passkeys PasskeyCredential[] back-reference on your User model.
  user         User    @relation(fields: [userId], references: [id])

  @@index([userId])
  @@map("passkey_credentials")
}

model PasskeyChallenge {
  sessionId String @id @map("session_id")
  challenge String
  expires   BigInt // unix milliseconds — BIGINT avoids INT4 overflow of Date.now()

  @@map("passkey_challenges")
}

IMPORTANT — back-reference required: Add a passkeys PasskeyCredential[] field to your User model. Prisma validates referential integrity in the schema DSL even for raw-SQL-only adapters; without the back-reference, prisma validate (and prisma migrate dev) will fail.

Then run your own migration:

npx prisma migrate dev --name add_passkey_tables

You own your data, so you own your migrations. This adapter ships no numbered .sql files — avoiding the 0013 collision problem seen when adapters ship their own numbered files that overlap with a consumer's migration counter.

How the Adapter Works

The adapter talks to the database entirely via parameterized raw SQL (prisma.$queryRaw / prisma.$executeRaw). It is independent of your generated client's model/delegate names — a published generic adapter cannot depend on consumer-specific generated models.

This means:

  • You pass usersTable, userIdColumn, and usernameColumn as plain strings (not Prisma model objects)
  • The adapter never calls prisma.passkeyCredential.* or prisma.passkeyChallenge.*
  • The .prisma schema fragment above is used only to drive your prisma migrate DDL generation, not by the adapter itself

usernameColumn UNIQUE Constraint

For the concurrency-resilient upsert to work correctly, usernameColumn (e.g. email) should have a UNIQUE constraint in your database. Without it, a race between two simultaneous registrations for the same new username could silently create duplicate rows.

In Prisma schema terms:

model User {
  id    String  @id
  email String  @unique   // <-- needed for safe upsert
  passkeys PasskeyCredential[]
}

Schema Changelog

SCHEMA_VERSION is exported from @glydi/passkey-store-prisma/schema so your app can assert compatibility at startup.

v2

Columns widened in passkey_credentials:

| Column | v1 Type (SQL / Prisma) | v2 Type (SQL / Prisma) | Reason | |--------|------------------------|------------------------|--------| | counter | INTEGER / Int | BIGINT / BigInt | WebAuthn signature counters are uint32 (max 4,294,967,295), which exceeds PostgreSQL INT4 max of 2,147,483,647 | | created_at | INTEGER / Int? | BIGINT / BigInt? | Stored as unix milliseconds; Date.now() ≈ 1.75 × 10¹² overflows INT4 |

Migration for existing deployments: Regenerate from PRISMA_SCHEMA (update your schema.prisma with the BigInt types below) and apply:

ALTER TABLE passkey_credentials
  ALTER COLUMN counter TYPE BIGINT,
  ALTER COLUMN created_at TYPE BIGINT;

This is a safe, backward-compatible widening — no data is lost and no application code changes are required.

SCHEMA_VERSION exported from @glydi/passkey-store-prisma/schema is now 2. You can assert compatibility at startup:

import { SCHEMA_VERSION } from "@glydi/passkey-store-prisma/schema";
assert(SCHEMA_VERSION === 2, "passkey schema version mismatch");

v1

Tables introduced: passkey_credentials, passkey_challenges

passkey_credentials

| Column | Type | Notes | |--------|------|-------| | credential_id | TEXT PRIMARY KEY | WebAuthn credential ID (base64url) | | user_id | TEXT NOT NULL | FK → your users table id column | | public_key | BYTEA NOT NULL | Raw COSE public key bytes | | counter | BIGINT NOT NULL DEFAULT 0 | Signature counter (replay protection) | | transports | TEXT | JSON-serialized array of AuthenticatorTransport | | device_type | TEXT | "singleDevice" or "multiDevice" | | backed_up | INTEGER | 1 = backed up, 0 = not, NULL = unknown | | name | TEXT | Consumer-supplied display name | | created_at | BIGINT | Unix milliseconds timestamp |

Index: idx_passkey_credentials_user on user_id. FK: fk_passkey_credentials_useruserIdColumn argument passed to createPrismaStore().

passkey_challenges

| Column | Type | Notes | |--------|------|-------| | session_id | TEXT PRIMARY KEY | Challenge session identifier | | challenge | TEXT NOT NULL | WebAuthn challenge (base64url) | | expires | BIGINT NOT NULL | Unix milliseconds — BIGINT avoids INT4 overflow of Date.now() |

expires is stored as unix milliseconds. BIGINT (not INTEGER) is required because Date.now() returns ~1.7 trillion ms, which exceeds PostgreSQL INT4's maximum of ~2.1 billion. The adapter uses lazy TTL eviction: expired challenges are deleted on read, no background sweeper.

Upgrade notes: No prior version exists. Fresh install — paste the models above into your schema.prisma and run prisma migrate dev after adding them.