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

@db4/schema

v0.1.2

Published

IceType schema compiler for db4 - concise, human-readable schema language with TypeScript inference

Readme

@db4/schema

(GitHub, npm)

Your schema is lying to you.

Types in TypeScript. Migrations in SQL. Validation scattered everywhere. When they disagree, you get runtime errors.

@db4/schema ends the chaos. Define once with IceType. Get types, migrations, and validation that never drift.

The Problem

Every database project follows the same path to pain:

  1. Write TypeScript interfaces
  2. Write SQL migrations
  3. Add validation at API boundaries
  4. Watch them slowly diverge
  5. Debug 3am production errors because email was supposed to be required
  6. Repeat forever

Teams make it worse. Someone updates the types, forgets the migration. Someone else edits the database directly. Your "single source of truth" becomes three sources of fiction.

The Solution: IceType

One schema. Every output. Zero drift.

import { parseSchema, defineSchema, InferType } from '@db4/schema'

// Full IceType syntax
const User = parseSchema({
  $type: 'User',
  id: 'uuid!',
  email: 'string! #unique',
  name: 'string!',
  avatar: 'string?',
  createdAt: 'timestamp! = now()',
  posts: '[Post] -> author',
})

// Simple syntax with type inference
const userSchema = defineSchema('User', {
  id: 'uuid',
  email: 'string',
  name: 'string',
  avatar: 'string?',
  age: 'int?',
})

type User = InferType<typeof userSchema>
// { id: string; email: string; name: string; avatar?: string; age?: number }

Quick Start

1. Install

npm install @db4/schema

2. Define

import { parseSchema, defineSchema, SchemaRegistry } from '@db4/schema'

const PostSchema = parseSchema({
  $type: 'Post',
  id: 'uuid!',
  title: 'string!',
  content: 'text!',
  published: 'boolean = false',
  authorId: 'uuid! #index',
  tags: '[string]?',
  viewCount: 'int = 0',
  embedding: 'vector[1536] ~> content',  // AI auto-embed
  createdAt: 'timestamp! = now()',
  $fts: ['title', 'content'],            // Full-text search
})

const TagSchema = defineSchema('Tag', {
  id: 'uuid',
  name: 'string',
  color: 'string?',
})

3. Validate

const registry = new SchemaRegistry()
registry.register(userSchema)

// Throws on invalid data
registry.validate('User', { id: '123', email: '[email protected]', name: 'Alice' })

// Or get detailed results
const result = registry.validateWithResult('User', someData)
if (!result.valid) {
  console.log(result.errors)
}

IceType Syntax

Reads like documentation. Compiles to everything.

Types

| Type | Description | TypeScript | |------|-------------|------------| | string | Text | string | | text | Long text | string | | int | Integer | number | | float | Decimal | number | | boolean | True/false | boolean | | uuid | UUID string | string | | timestamp | Date/time | Date | | date | Date only | Date | | time | Time only | Date | | json | Any JSON | unknown | | binary | Binary data | ArrayBuffer |

Modifiers

{
  required: 'string!',       // Required (!)
  optional: 'string?',       // Optional (?)
  indexed: 'string #index',  // Database index
  unique: 'string! #unique', // Unique constraint
  array: '[string]',         // Array
  withDefault: 'int = 0',    // Default value
}

Parametric Types

{
  price: 'decimal(10,2)',    // Precision
  code: 'varchar(10)',       // Fixed length
  embedding: 'vector[1536]', // Dimensions
}

Relations

{
  comments: '[Comment] -> post',  // Has many
  post: 'Post! <- comments',      // Belongs to
  similar: '~> content',          // AI semantic match
}

Computed Fields

{
  firstName: 'string!',
  lastName: 'string!',
  fullName: 'string := firstName ++ " " ++ lastName',
  displayName: 'string := nickname ?? firstName',
}

Directives

{
  $type: 'User',                    // Schema name
  $partitionBy: ['tenantId'],       // Partition key
  $fts: ['title', 'content'],       // Full-text search
  $vector: { embedding: 1536 },     // Vector index
  $index: [['email', 'tenantId']],  // Composite indexes
}

Schema Registry

Manages schemas with validation caching and lazy loading:

import { SchemaRegistry, defineSchema } from '@db4/schema'

const registry = new SchemaRegistry()

registry.register(userSchema)
registry.register(postSchema)

// Lazy loading for circular dependencies
registry.registerLazy('Comment', () => defineSchema('Comment', {
  id: 'uuid',
  content: 'string',
  authorId: 'uuid',
  postId: 'uuid',
}))

// Full validation details
const result = registry.validateWithResult('User', data)
// { valid: false, errors: [...], fieldsValidated: 5, durationMs: 2 }

// Cached validation (same object = instant)
registry.isValid('User', cachedUser)

Migrations

Generate migrations from schema changes:

import { diffSchemas, generateMigration, MigrationRunner } from '@db4/schema'

const diff = diffSchemas(oldSchema, newSchema)

if (diff.hasDestructiveChanges) {
  console.warn('Warning:', diff.warnings)
}

const migration = generateMigration(diff, {
  dialect: 'sqlite',  // or 'postgresql', 'mysql', 'd1'
  safeMode: true,
})

console.log(migration.up)   // ALTER TABLE statements
console.log(migration.down) // Rollback statements

const runner = new MigrationRunner(executor, { dialect: 'sqlite' })
await runner.initialize()
await runner.runPending(migrations)

Type Inference

Full TypeScript types from schema definitions:

import { defineSchema, InferType } from '@db4/schema'

const userSchema = defineSchema('User', {
  id: 'uuid',
  email: 'string',
  name: 'string',
  age: 'int?',
  tags: 'string[]',
  metadata: 'json?',
})

type User = InferType<typeof userSchema>
// {
//   id: string
//   email: string
//   name: string
//   age?: number
//   tags: string[]
//   metadata?: unknown
// }

Schema Discovery

Infer schemas from existing data:

import { inferType, inferSchema, discoverSchema } from '@db4/schema'

inferType('550e8400-e29b-41d4-a716-446655440000') // 'uuid'
inferType(42)                                      // 'int'
inferType('2024-01-15T10:30:00Z')                 // 'timestamp'

const docs = [
  { id: 1, name: 'Alice', email: '[email protected]' },
  { id: 2, name: 'Bob' }, // email missing
]
const schema = inferSchema(docs)
// { id: 'int', name: 'string', email: 'string?' }

Victory

With a unified schema:

  • TypeScript catches errors at compile time, not 3am
  • Migrations generate automatically
  • Validation stays in sync forever
  • Teams ship faster with fewer bugs
  • Sleep through the night

Failure

Without one:

  • Runtime errors from type mismatches
  • Manual migrations that drift from reality
  • Validation duplicated and inconsistent
  • "Worked yesterday" becomes your morning greeting
  • Tech debt compounds with every feature

API Reference

Parsing

  • parseField(fieldDef) - Parse a field definition
  • parseSchema(definition) - Parse a full schema
  • parseRelation(relDef) - Parse a relation
  • tokenize(input) - Tokenize IceType syntax

Schema Definition

  • defineSchema(name, fields) - Create a type-safe schema
  • InferType<S> - Infer TypeScript type from schema

Validation

  • SchemaRegistry - Schema management with caching
  • LazySchema - Deferred schema loading

Migrations

  • diffSchemas(from, to) - Compare IceType schemas
  • diffSimpleSchemas(from, to) - Compare simple schemas
  • generateMigration(diff, options) - Generate migration SQL
  • MigrationRunner - Execute and track migrations
  • MigrationTracker - Track applied migrations

Computed Fields

  • parseComputedExpression(expr) - Parse computed syntax
  • extractDependencies(ast) - Get field dependencies
  • checkDeterministic(ast) - Check if expression is pure
  • detectCircularDependencies(fields) - Find cycles
  • getComputationOrder(fields) - Topological sort
  • validateComputedDependencies(computed, fields) - Validate dependencies

Utilities

  • clearParserCaches() - Clear memoization caches
  • generateMigrationId() - Generate timestamp-based ID
  • computeChecksum(migration) - Compute migration checksum
  • generateMigrationTableSQL(dialect) - Generate tracking table DDL

License

MIT