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 🙏

© 2024 – Pkg Stats / Ryan Hefner

declassified

v1.0.1

Published

A package to help turn class-based libraries into functional ones.

Downloads

3

Readme

declassified

Motivation

A lot of libraries come in the form

class MyThing {
  constructor(config) {
    // much around with this
    this.thing = config[thing]
  }

  method() {
    // subtly depend on values set during the configuration phase
    something(this.config)
  }
}

This has a few problems:

  • The this.X properties are mutable throughout the code.
  • The constructor is synchronous meaning that often times the class isn't usable until some methods have been called.

What is preferable

What if, instead of a class, the authors had exported a library. It might look something like:

async function myThing (config) => {
  await doInitialization()
  return {
    method() {
      something(config)
    }
  }
}

This allows asynchronous set-up and protects the internals of the library (the configs, any other arguments) from imperative modification.

The solution

const declassified = require('declassified')
const MyClass = require('something')

const myLibrary = declassified(MyClassBasedLibrary, async (instance) => {
  // do imperative things to the instance here that you'd want to do as part of the setup
})

const myThing = await myLibrary(configs)

API

The declassify function takes only two arguments

  • The class-based library to be wrapped
  • (optional) An init function for your class which will be called with a "newed" class instance. The return value is used if supplied, otherwise it defaults to the class instance. Defaults to the identity function.

It returns an async function "config" that takes the same arguments as the original constructor.

When called the "config" function will return a frozen copy of the object returned by init

Examples

pg

Say you want to connect to a database like Postgres. The node pg client requires you to

const { Client } = require('pg')

const contructorArgs = { host: 'my-db' } //...
const client = new Client(contructorArgs)
await client.connect()

const res = await client.query('SELECT $1::text as message', ['Hello world!'])
console.log(res.rows[0].message) // Hello world!

How might we declassify that?

const declassified = require('declassified')
const { Client } = require('pg')

const contructorArgs = { host: 'my-db' } //...
const init = async (instance) => {
  await instance.connect()
  return instance
}

const client = await declassified(Client, init)(constructorArgs)

const res = await client.query('SELECT $1::text as message', ['Hello world!'])
console.log(res.rows[0].message) // Hello world!

So you might ask: this looks like it's just extra lines to do the same thing, but it's not: Say someone set client.connection = something. Instead of being meaningless code, that has broken your client permanently. It might not have even occurred during the handling of the request you're looking at due to the asynchronous nature of node. In large code bases (particularly large test suites where people are mocking and meta-programming frequently) these are real bugs that can be avoided.