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

flipchain

v0.6.0

Published

core chaining library, heavily based on [webpack-chain](https://github.com/mozilla-rpweb/webpack-chain)

Downloads

58

Readme

⛓ flipchain

NPM version MIT License fliphub flipfam

core chaining library, heavily based on webpack-chain, but not webpack-specific.

🏰 benefits

writing an api using flipchain means writing a single fluent api, but getting 3 apis as a result!

  • 🍉 rehydratable configurations
  • ⛓ fluent chainable api
  • 🍦 object configs that are easily merged deep

🌊 what?

fluent

imagine there are 2 people by the water, & the goal is to make 10 splashes.

as with most skills, you get better at skipping rocks the more that you do it. a lot of people cannot skip rocks, or they do not like to skip rocks, but they can still throw rocks and make a splash. think of this like non-fluent/vanilla-calls

Chain.prop()
Chain.longer()
Chain.intoSomeShapes()

or throwing a really huge rock into the water, and getting the splashes to make more splashes.

Chain.from({
  prop: null,
  longer: null,
  intoSomeShapes: null,
})

pebbles using method chaining looks similar to skipping rocks

Chain
  .prop()
  .longer()
  .intoSomeShapes()

writing an application with a fluent interface allows people to use it all three ways, and you only have to write it one way.

📘 examples

👋 intro

const ChainedMap = require('./ChainedMapExtendable')

class EasyFluent extends ChainedMap {
  constructor(parent) {
    super(parent)

    // extend a list of strings for easy chainable methods
    this.extend(['eh'])

    // same as .extend,
    // but when called with no arguments,
    // default is used (`true` in this case)
    // third param is optionally a prefix for inversified
    // for example, `no` => `noCanada()` for inverse value
    this.extendPrefixed(['canada'], true, 'no')
  }

  // if more advanced data changes are needed
  // or if the syntax is preferred for use with typescript or flowtype
  // .set, .get, .has are available
  igloo(igloo) {
    this.set('igloo', igloo)
    return this
  }

  toConfig() {
    return this.entries()
  }
}

// {igloo: 'fire', canada: false, eh: 'moose'}
const config = new EasyFluent()
  .igloo('fire')
  .noCanada()
  .eh('moose')
  .toConfig()

// this is == config
const hydrated = new EasyFluent()
  .from(config)
  .toConfig()

// canada is now true
const merged = new EasyFluent()
  .merge(config)
  .merge({canada: true})
  .toConfig()

🕳🏊 advanced

const ChainedMap = require('./ChainedMapExtendable')
const ChainedSet = require('./ChainedSet')

class Advanced extends ChainedMap {
  static init(parent) {
    return new Advanced(parent)
  }
  constructor(parent) {
    super(parent)
    this.list = new ChainedSet(this)
    this.extend(['eh'])
    this.extendWith(['canada'], true)
  }

  addName(name) {
    this.list.add(name)
    return this
  }

  igloo(igloo) {
    this.set('igloo', igloo)
    return this
  }

  toConfig() {
    return Object.assign(this.entries(), {
      list: this.list.values().map(name => name),
    })
  }

  // since we have additional data that is not simple key value
  // we do additional (albeit easy) steps to rehydrate
  from(obj) {
    super.from(obj)

    Object
      .keys(obj)
      .forEach(key => {
        const val = obj[key]
        switch (key) {
          case 'list': return val
            .filter(name => name)
            .forEach(name => this.addName(name))
        }
      })

    return this
  }

  // same with `from`
  // we do additional simple steps to merge in lists
  merge(obj) {
    Object
      .keys(obj)
      .filter(key => obj[key])
      .forEach(key => {
        const val = obj[key]
        switch (key) {
          case 'list': return val
            .filter(name => name)
            .forEach(v => this.addName(v))
        }
      })

    // built-in merging
    // can use `.mergeReal` to merge only `real` values
    // and `.merge` to merge any
    super.merge(obj)

    return this
  }
}

const chain = Advanced
  .init()
  .igloo('brr')
  .canada()
  .eh('eh!')
  .addName('thing one')
  .addName('thing two')

// true, `eh!`
chain.has('igloo')
chain.get('eh')

const result = chain.toConfig()

const hydrated = Advanced
  .init()
  .from(result)
  .toConfig()

const merged = Advanced
  .init()
  .merge(hydrated)
  .merge({igloo: 'whaaaat'})

// can use toConfig,
// and safely continue editing `merged`
// with a snapshot of the object data saved as `mergedResult`
const mergedResult = merged.toConfig()

// hydrated === result === {
//   igloo: 'brr',
//   canada: 'canada',
//   eh: 'eh!',
//   list: [ 'thing one', 'thing two' ]
// }

// merged === {
//   igloo: 'whaaaat',
//   canada: 'canada',
//   eh: 'eh!',
//   list: [ 'thing one', 'thing two' ]
// }

🌊 types

🌐 api

  • every chain has .className for easy debugging
  • every chain has this.parent
  • and this.parent is hidden on inspection by 🕵🗜 inspector-gadget for easier debugging

ChainedSet

  • Set
  • prepend => this
  • clear => this
  • delete => this
  • values => array of entries
  • has => boolean
  • merge => merge an object into the chain
  • when => conditional instance callback

ChainedMap

extendAlias

  • (methodsToAlias: Array<string>, methodToAlias: string, [thisArg])
  • alias a list of methods
  • @returns this

from

  • (obj: Object)
  • checks each property of the object
  • calls the chains accordingly
  • rehydrates a chain from an object

other

  • decorateParent (using childparent)
  • clear() => this // clearsAll
  • delete(key) => this
  • entries => {keysAndValues}
  • values => Object.values
  • get(key) => entry
  • has(key) => boolean
  • set(key, val) => this
if key is an array, merge in the value,
usually should use ChainedSet for this
  • concat(key, val) => this
  • append(key, val) => this
merging
  • mergeReal(obj) => this // only merges non-undefined values
  • merge(obj) => this
  • clean => this
  • when => conditional instance callback

🔗 links & more

chainMapTill

lets you chain until the required keys are set via chains, or if they are passed in, then it auto returns parent

chainedMapExtendable

  • has chains with .extends able to use default values when calling it
  • also can add prefixes (default no) so if you use cache default true, it can add noCache which does the inverse
  • set up for being chains of chains when you add a few decorating chains dynamically

📝🌊 TODO

  // using `izz` to validate types when setting
  this.extendType(['str'], 'string')