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

o-toolbox

v7.7.0

Published

Utilities for Node.js.

Downloads

12

Readme

Toolbox

Utilities for Node.js.

Installation

npm install o-toolbox

Documentation

http://o-programming-language.org/

Usage

Type

Utility to query the type of an object

const {Type} = require('o-toolbox')

Type.isUndefined(object)
Type.isNull(object)
Type.isBoolean(object)
Type.isString(object)
Type.isInteger(object)
Type.isFloat(object)
Type.isFunction(object)
Type.isArray(object)
Type.isObject(object)
Type.isPromiseAlike(object)
Type.respondsTo(object, methodName)

Using typeof has some problems. typeof(null) === 'object', Number.isInteger() exists but Number.isFloat() does not and there is no type boolean to name a few.

This utility aims to address some of these issues.

Using instanceof is also discouraged since it does not allow full polymorphism. Instead of asking for the class of an object in some cases asking whether the object responds to message or not might be a better choice.

To ask that there's the method

const {Type} = require('o-toolbox')

Type.repondsTo(object, methodName)

Finally there's the method

Type.displayString(object)

which returns a string representation of the object suitable for debugging messages. For example

const {Type} = require('o-toolbox')

Type.displayString(undefined) === 'undefined'
Type.displayString(null) === 'null'
Type.displayString('a string') === "'a string'"
Type.displayString(new User()) === "a User"

Copy

Copies addresses the copy of an object without having to check first the type of the object. It copies undefined and null values, basic type values, arrays, objects and class instances.

To make a shallow copy of an object do

Copy.shallow(null)
Copy.shallow(1)
Copy.shallow({a: 1})
Copy.shallow(new SomeObject())
Copy.shallow([1,2,3])

If the object is an instance of a class it's expected to implement a .copy() method

class SomeObject {
  copy() {
    return new this.constructor(this.props)
  }
}

To make a deep copy of an object use

Copy.deep(null)
Copy.deep(1)
Copy.deep({a: 1})
Copy.deep(new SomeObject())
Copy.deep([1,2,3])

If the object is an instance of a class it's expected to implement a .copy() and/or a .deepCopy() method

const Copy = require('o-toolbox')

class SomeObject {
  copy() {
    return new this.constructor(this.props)
  }

  deepCopy() {
    const propsCopy = this.props.map((prop)=> Copy.deep(prop))
    return new this.constructor(propsCopy)
  }
}

Dictionary

A subclass of js Map.

It handles Map methods and adds the protocol

const {Dictionary} = require('o-toolbox')

const dictionary = new Dictionary()

dictionary.isEmpty()
dictionary.getKeys()
dictionary.getValues()
dictionary.defines({ key: 'someKey' })
dictionary.define({ key: 'someKey', value: 10 })

dictionary.at ({ key: 'someKey'})
dictionary.at ({
  key: 'someKey',
  ifPresent: (value) => { ... }
})
dictionary.at ({
  key: 'someKey',
  ifAbsent: () => { ... }
})
dictionary.at ({
  key: 'someKey',
  ifPresent: (value) => { ... },
  ifAbsent: () => { ... }
})

dictionary.mergeKeysFrom ({prop1: 1, prop2: 2})
dictionary.mergeKeysFromDictionary(otherDictionary)

// shallow copy
dictionary.copy(anotherDictionary)

// The following methods are sync and will not work as expected with async code.

dictionary.keysDo((key) => {
  // ...
})
dictionary.valuesDo((key) => {
  // ...
})
dictionary.keysAndValuesDo((key, value) => {
  // ...
})

Set

A subclass of js Set.

It handles Set methods and also adds the protocol

const {Set} = require('o-toolbox')

const set = new Set()

set.isEmpty()
set.includes(item)
set.toArray()
set.equals(anotherSet)
set.contains(anotherSet)
set.intersects(anotherSet)
set.addAll(items)
set.remove(item)
set.removeAll(items)
set.union(anotherSet)
set.difference(anotherSet)
set.intersection(anotherSet)
set.copy()

SequentialCollection

An Array like object, with convenience methods and validations of boundaries, and polymorphic with other collections in this library

const {SequentialCollection} = require('o-toolbox')

const sequentialCollection = new SequentialCollection()
const sequentialCollection = SequentialCollection.with(item)
const sequentialCollection = SequentialCollection.withAll(items)

sequentialCollection.add(item)
sequentialCollection.addAll(items)
sequentialCollection.at({ index: 1 }) // raises an error if index 1 is out of bounds
sequentialCollection.at({ index: 1, ifAbsent: () => null }) // returns null if index 1 is out of bounds
sequentialCollection.countAllThat( (item, i) => item > 1)
sequentialCollection.clear()
sequentialCollection.filter( (item, i) => item > 1 )
sequentialCollection.forEach( (item, i) => {
  // ...
})
sequentialCollection.forEachInReverseOrder( (item, i) => {
  // ...
})
sequentialCollection.getFirst() // raises an error if empty
sequentialCollection.getFirstIndexThat({ block: (item, i) => item > 1 })
sequentialCollection.getFirstThat({ block: (item, i) => item > 1 })
sequentialCollection.getIndexOf(item)
sequentialCollection.getSize()
sequentialCollection.getLast() // raises an error if empty
sequentialCollection.isEmpty()
sequentialCollection.map( (item, i) => {
  return // ...
})
sequentialCollection.remove(item)
sequentialCollection.removeAll(items)
sequentialCollection.removeAllThat( (item, i) => item > 1 )
const first = sequentialCollection.removeFirst()
const item = sequentialCollection.removeItemAt({ index: 1 })
const last = sequentialCollection.removeLast()
sequentialCollection.sortUsing({ block: (item1, item2) => {
  if (item1 < item2) { return -1 }
  if (item1 > item2) { return 1 }
  return 0
})
sequentialCollection.toArray()

SortedCollection

A SequentialCollection that ensures the order of its items

Its interface is the same as SequentialCollection, except for the creation of the collection, which takes a sort block

const {SortedCollection} = require('o-toolbox')

const sortedCollection = new SortedCollection()

const sortedCollection = new SortedCollection({
  sortBlock: (item1, item2) => {
    if (item1 < item2) { return -1 }
    if (item1 > item2) { return 1 }
    return 0
  }
})

const sortedCollection = SequentialCollection.with({
  item: item,
  sortBlock: (item1, item2) => {
    if (item1 < item2) { return -1 }
    if (item1 > item2) { return 1 }
    return 0
  }
})

const sortedCollection = SequentialCollection.withAll({
  items: items,
  sortBlock: (item1, item2) => {
    if (item1 < item2) { return -1 }
    if (item1 > item2) { return 1 }
    return 0
  }
})

Path

A path in the file system.

Paths are immutable values, meaning that no Path method changes its state. When a modification is done it returns a new Path object.

// Path is not included in index.js for it depends on 'path' and 'fs' modules
const Path = require('o-toolbox/src/Path')

const path = new Path('/etc/logs/app.log')

path.exists()
path.isDirectory()
path.isFile()

// Returns true of the path string matches the given regex
path.matches(regex)

path.toString()

// subPath can be a String or a Path
path.append(subPath)
path.back()
path.back({ n: 2 })

path.getStats()
path.getFileExtension()
path.getFileExtension({dot: false})
path.getFilename()
path.getFilename({ extension: true })

path.getBasePath()
path.getSegments()
path.getFirstSegment()
path.getFirstSegments({ n: 2 })
path.getLastSegment()
path.getLastSegments({ n: 2 })
path.toAbsolutePath()


path.getFiles()
path.getFiles({ recursive: true })
path.getFiles({ matching: /[.]log^/ })
path.getFiles({ recursive: true, matching: /[.]log^/ })

// Returns the contents of the file as a String.
// Raises an error if the file does not exist
path.getFileContents()
// Returns the contents of the file as a JSON object.
// Raises an error if the file does not exist
// Raises an error if the file contents is not a valid JSON string
path.getJSONFileContents()

// Writes the given contents to the file
// Returns the path object.
// Creates the file if it does not exist
// Overrides the file if it exists
// Raises an error if the file directory does not exist
path.writeFileContents("File contents")
// Writes the given object as a JSON string to the file
// Returns the path object.
// Creates the file if it does not exist
// Overrides the file if it exists
// Raises an error if the file directory does not exist
path.writeJSONFileContents(object)

// Deletes the file
// Raises an error if the path does not exist
// Raises an error if the path is a directory
path.deleteFile()
// Deletes the file
// Does nothing if the path does not exist
// Raises an error if the path is a directory
path.ensureDeleteFile()

path.getDirectories()
path.getDirectories({ recursive: true })
path.getDirectories({ matching: /[.]log^/ })
path.getDirectories({ recursive: true, matching: /[.]log^/ })

// Creates all the directories in the Path
// If the directory exists does nothing
// If the directory partially exists creates the missing subdiretories
path.createDirectory()

path.files({
  do: (filePath) => { ... }
})
path.files({
  recursive: true,
  do: (filePath) => { ... }
})
path.files({
  recursive: true,
  matching: /[.]log^/,
  do: (filePath) => { ... }
})

path.directories({
  do: (dirPath) => { ... }
})
path.directories({
  recursive: true,
  do: (dirPath) => { ... }
})
path.directories({
  recursive: true,
  matching: /[.]log^/,
  do: (dirPath) => { ... }
})

path.filesAndDirectories({
  do: (dirPath) => { ... }
})
path.filesAndDirectories({
  recursive: true,
  do: (dirPath) => { ... }
})
path.filesAndDirectories({
  recursive: true,
  matching: /[.]log^/,
  do: (dirPath) => { ... }
})

Environment

Class to conditionally evaluate blocks of code depending on the environemnt where the script is running

The Environment object receives the values in process.env or a copy with the variables of interest and provides methods for evaluating functions only in certain environments:

const {Environment} = require('o-toolbox')
const environment = Environment({ env: process.env })

environment.isProduction()  // env.NODE_ENV = 'production'
environment.isDevelopment() // env.NODE_ENV = 'development'
environment.isTesting()     // env.NODE_ENV = 'testing'

environment.on({
  development: () => { ... },
  staging: () => { ... },
  production: () => { ... },
  default: () => { ... }
})

environment.on({
  development: () => app.use('/', debuggingRoutes)
})

Errors

A hierarchy of generic errors

RuntimeError
NotYetImplementedError
SubclassResponsibilityError
AbsentKeyError
AbsentMethodError
PathDoesNotExistError
UnexpectedTypeError
ParamsError
MissingRequiredParamError
UnexpectedParamError

Each of these errors can be raised with sync Exception or with an async Promise rejection

const {RuntimeError} = require('o-toolbox')

RuntimeError.raise({ message: 'Error message here ...' })
RuntimeError.raise({ message: 'Error message here ...', async: true })

// which is the same as doing
new RuntimeError().raise({ message: 'Error message here ...' })

The following errors can be raised also with arguments using the method .raiseOn(...)

const {
  SubclassResponsibilityError,
  NotYetImplementedError
  MissingRequiredParamError,
  UnexpectedParamError
} = require('o-toolbox')

SubclassResponsibilityError.raiseOn({ methodName: 'someMethod', className: 'SomeClass' })
NotYetImplementedError.raiseOn({ methodName: 'someMethod', className: 'SomeClass' })
MissingRequiredParamError.raiseIfMissing({ param: n, paramName: 'n' })
MissingRequiredParamError.raiseOn({ paramName: 'n' })
UnexpectedParamError.raiseOn({ paramName: 'n' })

ImportMethods

ImportMethods utility is a simplified implementation of Mixins.

It lacks of inheritance with other Mixins applied to the same class and does not keep its own set of instance variables but in many circumstances it still stands as a good enough solution.

To import the methods of a Mixin class into a concrete class do

// A mixin to add an .equals(other) method to a class
class ComparableValueBehaviour {
  equals (other) {
    return this.getValue() == other.getValue()
  }
}
const {ImportMethods} = require('o-toolbox')
const {ComparableValueBehaviour} = require('./ComparableValueBehaviour')

// A class
class CustomValue {
  constructor(value) {
    this.value = value
  }

  getValue() {
    return this.value
  }
}
// The addition of the mixin into the class
ImportMethods.all({ from: ComparableValueBehaviour, into: CustomValue })

// Now CustomValue instances respond to .equals()
const value1 = new CustomValue(1)
const value2 = new CustomValue(2)
value1.equals(value2)

ImportMethods also supports partial imports with

ImportMethods.all({
  from: ComparableValueBehaviour,
  into: CustomValue,
  except: ['someMethod', 'anotherMethod']
})

ImportMethods.methods({
  methodNames: ['someMethod', 'anotherMethod']
  from: ComparableValueBehaviour,
  into: CustomValue
})

ImportMethods.method({
  methodName: 'someMethod',
  from: ComparableValueBehaviour,
  into: CustomValue
})

If possible it's encouraged to encapsulate the use of ImportMethods in a static method in the mixin to simplify its usage and avoid an additional require statement

const {ImportMethods} = require('o-toolbox')

// A mixin to add an .equals(other) method
class ComparableValueBehaviour {
  static attachTo(aClass) {
    ImportMethods.all({ from: this, into: aClass })
  }

  equals (other) {
    return this.getValue() == other.getValue()
  }
}
const {ComparableValueBehaviour} = require('./ComparableValueBehaviour')

// A class
class CustomValue {
  constructor(value) {
    this.value = value
  }

  getValue() {
    return this.value
  }
}
ComparableValueBehaviour.attachTo(CustomValue)

// Now CustomValue instances respond to .equals()
const value1 = new CustomValue(1)
const value2 = new CustomValue(2)
value1.equals(value2)

NoOverridesImportMethods

NoOverridesImportMethods behaves like ImportMethods but if a method is defined in the target class it does not override it.

If the mixin provides default implementation of methods overriden in the target classes you should use NoOverridesImportMethods.all or ImportMethods.all({ except: }).

Protocol

Protocol utility validates that a class defines all the methods in a given protocol.

To use it define a protocol to validate

const {Protocol} = require('o-toolbox')
class ComparableValueProtocol extends Protocol {
  equals(other) {}
  above(other) {}
  below(other) {}
}

and with it validate that a class implements all its methods

ComparableValueProtocol.validate(SomeClass)

If the validation fails it raises an error.

If you prefer to ask for a boolean instead of raising an error use

ComparableValueProtocol.isFulfilledBy(SomeClass)

Typically you would validate the class right after its definition

const ComparableValueProtocol = require('./ComparableValueProtocol')

class SomeClass {
  // ...
}

ComparableValueProtocol.validate(SomeClass)

Classes usually have 2 protocols. An implementation protocol stating the methods a subclass must implement and a public protocol stating the methods a user of the class can call.

The recomendation is to define and validate each protocol independently with its own Protocol class.

Random

Utility to generate random numbers

Important

This random number generator is not a good random generator and it's not suitable for statistical calculations, nor to use it in cryptographics

For not critical uses, like graphical animations and games, it's good enough

Generate a random number with any of the following methods:

const { Random } = require('o-toolbox')

const randomFloat = Random.numberBetween( 1, 10 ) // 1 and 10 are included
const randomInteger = Random.integerBetween( 1, 10 ) // 1 and 10 are included
const randomItem = Random.pickFrom( [ 'a', 'b', 'c' ] )

DoMe commands

DoMe commands are intended to be self-documented, please take a look at the files in DoMe/forDevelopment/inWindows, or in DoMe/forDevelopment/inDocker