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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@rvoh/dream

v0.39.0

Published

dream orm

Downloads

8,995

Readme

ATTENTION: we are currently in the process of releasing this code to the world, as of the afternoon of March 10th, 2025. This notice will be removed, and the version of this repo will be bumped to 1.0.0, once all of the repos have been migrated to the new spaces and we can verify that it is all working. This is anticipated to take 1 day.

Dream is a typescript-driven, esm-first ORM built on top of kysely. It is built to operate within the Psychic web framework, but can be brought into other projects and used without the encapsulating framework (though this is theoretical, and we do not encourage it at this time). It is actively being developed to support production-grade applications in use within the RVOHealth organization, who has kindly sponsored the continued development of this ORM, as well as the Psychic web framework as a whole.

Documentation: https://psychic-docs.netlify.app Psychic web framework github: https://github.com/rvohealth/psychic

Features

The dream ORM features:

  • static query building engine
const records = await User.where({ email: 'fred@fred' }).all()
// User[]

const user = await User.where({ email: 'fred@fred' }).first()
const user = await User.where({ email: 'fred@fred' }).last()
// User | null

const user = await User.where({ email: ops.like('%fred@%') })
  .order('id', 'desc')
  .limit(3)
  .all()

// User[]
  • model hooks
    • before create
    • before update
    • before delete
    • before save
    • after create
    • after update
    • after delete
    • after save
// models/composition.ts

class Composition {
  ...
  @deco.BeforeCreate()
  public setDefaultContent() {
    if (!this.content) this.content = 'default content'
  }

  @deco.AfterCreate()
  public conditionallyChangeContentOnCreate() {
    if (this.content === 'change me after create') this.content = 'changed after create'
  }

  @deco.AfterUpdate()
  public conditionallyChangeContentOnUpdate() {
    if (this.content === 'change me after update') this.content = 'changed after update'
  }

  @deco.AfterSave()
  public conditionallyChangeContentOnSave() {
    if (this.content === 'change me after save') this.content = 'changed after save'
  }
  ...
}
  • validations
    • presence
    • length{min, max}
    • contains{string | regex}
export default class User extends Dream {
  ...
  @deco.Validates('contains', '@')
  @deco.Validates('presence')
  public email: string

  @deco.Validates('length', { min: 4, max: 18 })
  public password: string
  ...
}
  • associations

    • belongs to
    • has one
    • has many
    • has one through
    • has many through
    • has one through (nested indefinitely)
    • has many through (nested indefinitely)
// models/user.ts
class User {
  ...
  @deco.HasMany('Composition')
  public compositions: Composition[]

  @deco.HasOne('Composition')
  public mainComposition: Composition

  @deco.HasMany('CompositionAsset', {
    through: 'compositions',
  })
  public compositionAssets: CompositionAsset[]
}

// models/composition.ts
export default class Composition extends Dream {
  ...
  @deco.BelongsTo('User')
  public user: User
}

// models/composition-asset.ts
export default class CompositionAsset extends Dream {
  ...
  @deco.BelongsTo('Composition')
  public composition: Composition
  ...
}

  • scopes

    • named
    • default
  • single table inheritance

class User {
  @deco.Scope()
  public static withFunnyName(query: Query<User>) {
    return query.where({ name: 'Chalupas jr' })
  }

  // this will always fire whenever queries are run against the model
  @deco.Scope({ default: true })
  public static hideDeleted(query: Query<User>) {
    return query.where({ deleted_at: null })
  }
}

User.scope('withFunnyName')
// will only return records with the name "Chalupas jr"

REPL

the repl will open a console, exposing access to the test app models that are built into the framework. this test app is used to run all of our assertions, and is meant to be replaced by an actual app consuming this ORM.

to get into the console, type:

yarn console

Once inside the repl, you are able to access all the models within the test app. All the classes are automagically imported.

const user = await User.create({ email: 'hello@there', password: 'howyadoin' })
user.email = 'fred@flinstone'
await user.save()

Specs

Running specs is very easy, not much to explain here.

yarn spec

We are using jest under the hood, and have a plugin enabled called jest-plugin-context, which allows us to branch specs out using the context function in place of describe, like so:

describe('Dream#pluck', () => {
  let user1: User
  let user2: User
  beforeEach(async () => {
    user1 = await User.create({ email: 'fred@frewd', password: 'howyadoin' })
    user2 = await User.create({ email: 'how@yadoin', password: 'howyadoin' })
  })

  it('plucks the specified attributes and returns them as raw data', async () => {
    const records = await User.pluck('id')
    expect(records).toEqual([user1.id, user2.id])
  })

  context('with multiple fields', () => {
    it('should return multi-dimensional array', async () => {
      const records = await User.order('id').pluck('id', 'createdAt')
      expect(records).toEqual([
        [user1.id, user1.created_at],
        [user2.id, user2.created_at],
      ])
    })
  })
})

CLI

yarn build
# builds source code using the typescript compiler, sending it into the dist folder

yarn spec
# runs core development specs (same as yarn dream spec --core)

SQL_LOGGING=1 yarn spec
# runs core development specs, printing SQL queries

yarn console
# opens console, providing access to the internal test-app/app/models folder (same as yarn dream console --core)

yarn dream
# opens a small node cli program, which right now only provides commands for generating dreams and migrations,
# but which will likely eventually encompass all of the above commands

yarn dream db:migrate --core
# runs migrations for core development. Use this if you are working on the dream ORM source code.

yarn dream db:migrate
# runs migrations for an app that is consuming the dream ORM. This is meant to be run from another repo,
# e.g. within a different app's package.json:
#
#  "scripts": {
#     "db:migrate": "yarn --cwd=node_modules/dream db:migrate"
# }

yarn dream sync:schema
# syncs db schema from an app that is consuming the dream ORM. This is necessary, because in order to provide
# deep integration with kysely, we must actually introspect the schema of your app and provide it
# as part of the dream orm source code build. It essentially scans your database, examines all tables, outputs interfaces that kysely can consume, puts them into a file, and exports all the interfaces within that file.

# the sync command is already run as part of the migration sequence, so there should be no need to
# run it manually. Simply run migrations to have the syncing done for you.

# A copy of this is output to your app (though don't bother manually modifying it, since it is blown away each
# time you run migrations). It is then copied over to the node_modules/dream/src/sync folder, so that importing
# the dream orm can provide that integration for you.

# This is meant to be run from another repo,
# e.g. within a different app's package.json:
#
#  "scripts": {
#     "sync:schema": "yarn --cwd=node_modules/dream sync:schema"
# }

yarn dream sync:schema --core
# runs the same sync script mentioned above, but for core development. You would run this if you were trying to
# sync for the test-app, which is used to seed the local tests and provide models for use within the console.
# Similar to above, a local copy of schema is kept within test-app/types/dream.ts, which is updated each time you
# run yarn core:db:migrate.

yarn dream sync:associations
# runs a script which analyzes your models, building a mapping of the associations into typescript interfaces
# which will assist in providing enforced type completion mechanisms at the code composition level. This must be
# this is used by the underlying `load` method, as well as `preload` and `joins` methods within the query
# building layer. This is all copied to the file specified in the `.dream.yml#associations_path`. This is also automatically done whenever you run migrations, run specs, or open a console

yarn dream sync:associations --core
# same as above, but copies the associations.ts file to test-app/db/associations.ts

yarn dream db:drop --core
# drops the db for either the core app or a consuming app (which is why there is no "core:db:drop" variant)

yarn dream db:create --core
# creates the db for either the core app or a consuming app (which is why there is no "core:db:create" variant)

Similarity matching

Dream provides a similarity searching library out of the box which allows you to implement fuzzy-searching in your app. To use it, first write a migration.

yarn psy g:migration add_fuzzy_search_to_users

Open the generated migration, create the pg_trgm extension and create a gin index for a field on your model

import { Kysely, sql } from 'kysely'
import { createGinIndex, createExtension } from '@rvoh/dream'

export async function up(db: Kysely<any>): Promise<void> {
  // this only needs to be run once per db
  await createExtension('pg_trgm', db)

  // this is not necessary, but it will dramatically improve the search
  // speed once your db has enough records for it to matter
  await createGinIndex('users', 'name', 'users_name_gin_index', db)
}

export async function down(db: Kysely<any>): Promise<void> {
  await db.schema.dropIndex('users_name_gin_index')
}

Once done, run migrations

NODE_ENV=development yarn psy db:migrate

Now you can take full advantage of pg_trgm using the dream adapters!

import { ops } from '@rvoh/dream'

// applies a score match of 0.3
const users = await User.where({ name: ops.similarity('steeven hawkins') }).all()

// applies a score match of 0.5
const users = await User.where({ name: ops.wordSimilarity('steeven hawkins') }).all()

// applies a score match of 0.6
const users = await User.where({ name: ops.strictWordSimilarity('steeven hawkins') }).all()

// applies a score match of 0.2
const users = await User.where({ name: ops.similarity('steeven hawkins', { score: 0.2 }) }).all()

// this will also work for plucking, updating, joining, and aggregate functions like min, max and count
const userIds = await User.where({ name: ops.similarity('steeven hawkins') }).pluck('id')
const min = await User.where({ name: ops.similarity('steeven hawkins') }).min('id')
const max = await User.where({ name: ops.similarity('steeven hawkins') }).max('id')
const count = await User.where({ name: ops.similarity('steeven hawkins') }).count()
const petIds = await User.innerJoin('pets', { name: ops.similarity('fido') }).pluck('pets.id')
const numUpdatedRecords = await User.where({ name: ops.similarity('steeven hawkins') }).update({
  name: 'Stephen Hawking',
})
const users = await User.innerJoin('pets', { name: ops.similarity('fido') }).all()

const user = await User.first()
const pets = await user
  .associationQuery('pets')
  .where({ name: ops.similarity('fido') })
  .all()

Hidden gotchas

  • STI descendants of the same root model that define the same association must define that association identically if they are used in joins, preload, or load. For example, the following will not work properly:
@STI(A)
class B extends A {
  @deco.HasMany('X')
  public xx: X[]
}

@STI(A)
class C extends A {
  @deco.HasMany('X', { and: { something: true } })
  public xx: X[]
}

class Z extends Dream {
  @deco.HasMany('A')
  public aa: A[]
}

// this will not work as expected because the HasMany('X') are defined differently
await z.load({ aa: 'xx' }).execute()

Questions?

Dream is an open source library, so we encourage you to actively contribute. Visit our Contributing guide to learn more about the processes we use for submitting pull requests or issues.

Are you trying to report a possible security vulnerability? Visit our Security Policy for guidelines about how to proceed.