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

@rafikidota/serpens

v2.3.0

Published

Sometimes, the best way to solve your own problems is to help someone else.

Readme

TypeORM Snake Naming Strategy

Sometimes, the best way to solve your own problems is to help someone else.

@rafikidota/serpens ships a TypeORM SnakeNamingStrategy plus three standalone string-case utilities (camelCase, snakeCase, titleCase) you can use independently of TypeORM.

Installation

npm install @rafikidota/serpens typeorm
# or
pnpm add @rafikidota/serpens typeorm

Requirements:

  • typeorm ^1.1.0 — peer dependency, bring your own install.
  • Node.js 24+ (the version used in CI, see .nvmrc).

The package is published as ESM-first with a dual build: import resolves to dist/index.mjs, require to dist/index.cjs, each with its own type declarations.

Using SnakeNamingStrategy with TypeORM

import { DataSource, DataSourceOptions } from 'typeorm';
import { SnakeNamingStrategy } from '@rafikidota/serpens';

const config: DataSourceOptions = {
    type: 'postgres',
    host: 'localhost',
    port: 5432,
    database: 'postgres',
    username: 'postgres',
    password: 'postgres',
    entities: [__dirname + '/**/*.entity{.ts,.js}'],
    migrations: [__dirname + '/migrations/*{.ts,.js}'],
    synchronize: false,
    namingStrategy: new SnakeNamingStrategy(),
};

export default new DataSource(config);

With this strategy applied, an entity like:

@Entity()
class UserProfile {
  @Column()
  firstName: string;
}

maps to table user_profile, column first_name.

The strategy overrides three members of TypeORM's DefaultNamingStrategy:

| Member | Behaviour | | --- | --- | | tableName | Explicit customName wins, otherwise snakeCase(className) | | columnName | Joins embedded prefixes with the column name, then snake-cases the result | | relationName | snakeCase(propertyName) |

Everything else falls back to DefaultNamingStrategy (index names, foreign keys, join tables, etc.).

String-case utilities

The same conversion helpers used internally by SnakeNamingStrategy are exported for standalone use — no TypeORM required.

import { camelCase, snakeCase, titleCase } from '@rafikidota/serpens';

snakeCase('firstName');           // 'first_name'
snakeCase('UserHTTPServer');       // 'user_http_server'

camelCase('first_name');          // 'firstName'
camelCase('first_name', true);    // 'FirstName'

titleCase('first name');          // 'First Name'

Development

This package uses pnpm (version pinned via packageManager in package.json), Vitest and tsdown.

pnpm install
pnpm typecheck   # tsc --noEmit
pnpm test        # run the test suite (pnpm test:watch for watch mode)
pnpm lint        # lint and auto-fix
pnpm lint:check  # lint without fixing (CI variant)
pnpm format      # prettier --write
pnpm build       # build dual ESM/CJS output to dist/

A husky pre-commit hook runs lint-staged over staged .ts/.json files (prettier, then eslint).

Releasing

CI (GitHub Actions) runs typecheck, lint, test and build on pushes to main/development and on every pull request.

To cut a release:

pnpm version patch         # or minor / major — updates package.json, commits, tags
git push --follow-tags     # pushing the v* tag triggers the publish workflow

Pushing a v* tag runs three jobs:

| Job | Does | | --- | --- | | ci | Reruns the full CI workflow as a reusable workflow | | guard | Fails if the tag doesn't match package.json, then resolves the npm dist-tag — prereleases go to next, stable versions to latest | | publish | Waits for maintainer approval, then publishes |

The publish job runs in the release environment, so it pauses for manual approval with the CI and guard results already visible. It publishes with provenance via OIDC trusted publishing — no npm token secret involved — and only dist/ is included in the tarball.

Use pnpm version rather than editing package.json by hand: it keeps the tag and the version in sync, which is what guard checks. The dist-tag matters because a trusted-publishing token cannot change dist-tags after the fact, so a mistagged prerelease would stay on latest permanently.

Additional Resources