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

moosejs

v1.8.0

Published

A postmodern object system for JavaScript

Downloads

32

Readme

MooseJS

A postmodern object system for JavaScript

Build status Licence Issues

Install

$ npm install moosejs

Features

  • Statically typed classes
    • Quick type and consistency checking
    • Trigger system provides event based logic
    • Serialize and de-serialize object structures quickly
    • Property type casting
  • Typed arrays and maps
    • Ensure value and key type
  • Enumerators
    • Simple system for ensuring string content
  • Methods
    • Runtime strictly-typed methods
  • No dependancies.

Description

MooseJS is inspired by the Perl library 'Moose'. The project started as a way to parse large, complex config structures without having to repeat basic type checking and quickly incorporated type validation.

Feedback

If you find any bugs in MooseJS please let me know at: [email protected] Or to let me know what you think and suggest features: [email protected]

Alternatively you can post issues on the GitHub repo

Wiki

The complete documentation can be found on the MooseJS Wiki

Overview

Classes

const MooseJS = require('moosejs')

class Student extends MooseJS.defineClass({
    final: true,
    has: {
        name:   { is:"ro", isa:String,   required:true },
        grades: { is:"rw", isa:[Number], required:true, default:[] },
        school: { is:"rw", isa:School,   enumerable:false },
    }
})
{
    get grade_average(){
        return this.grades.reduce((a, b) => a + b) / this.grades.length
    }
}

const bob = new Student({
    name:"Bob McBobson",
    grades: [1.0, 1.2, 0.8]
})

bob.grades.push({ a:1 }) // Err. Exception thrown!
bob.grades.push("1.2")   // string converted to Number.

delete bob.grades // Err. Required property!
bob.grades = undefined // Err. Nope still required!

bob.name += " the 3rd" // Err. Read-only property.

// Object can be fully serialized and parsed.
const evilBob = new Student( JSON.parse( JSON.stringify( bob ) ) ) 

If that get's boring you can specify just the type for a short-hand decleration. Short-hand syntax properties are always 'required' and 'ro'.


// These two are equivelent

const Person = MooseJS.defineClass({
	final: true,
	has: {
		name: { is:"ro", isa:String, required:true },
	},
})

const Person = MooseJS.defineClass({
	final: true,
	has: {
		name: String,
	},
})

const Test = new Person({ name:"Testy McTesterson" })

Typed Arrays

Typed arrays allow you to create type strict arrays.

Configurable properties include: is: "rw" for a mutable array or "ro" for an immutable array isa: Type type that each array member must be an instance of. maximum: Maximum length of the array minimum: Minimum length of the array trigger: A function to call when the array changes. If an error thrown the array will revert to it's previous state. Input arguments are:

  • Full array post change.
  • New value being added (or removed)
  • Old value at this position in the array
  • Index of the change in the array
const DateArray = new MooseJS.TypedArray(Date)

const birthdays = new DateArray(['1982-05-20'])           // Converted.
birthdays.push('2011-09-12T21:25:41')                     // Yep this too.
birthdays[2] = 'Wed Sep 12 2018 21:25:41 GMT+0100 (BST)') // And this!

// Or for something more advanced

const GradeArray = new MooseJS.TypedArray({
    is: "ro", // Can't be changed after initialization
    isa: Number, // Must contain only numbers
    minimum: 1,
    trigger: (array, newValue, oldValue, i) => {
        const average = array.reduce((l, r) => l + r, 0) / array.length
        if (average < 50){
            throw Error("Grade average too low")
        }
    }
})

const myGrades = new GradeArray([ 1.2, 4.2 ]) // Okay up to 50

myGrades.push(0.1) // Err. Read-only

// TypedArrays can also be specified as properties with shorthand syntax
// just place a valid constructor in an array of 1.
// 'is' property (but not triggers) will be delegated to the TypedArray

const Foo = MooseJS.defineClass({
    has: {
        bar: { is:"rw", isa:[Number] }, // Equivelent to TypedArray({ is:"rw", isa:Number })
        baz: { is:"ro", isa:[Number] }, // Equivelent to TypedArray({ is:"ro", isa:Number })
    }
})

const foo = new Foo({
    bar: [1,2,3],
    baz: [4,5,6],
})

foo.bar.push("4") // Okay !
foo.baz.push(7) // Err. Read-only

TypedMaps

const NumberMap = new MooseJS.TypedMap({ key:String, value:Number })

const numbers = new NumberMap({ one:1, two:"2" }) // Converted
numbers.set("three", "3.0")                       // Also converted

// Copy constructable
const copyNumbers = new NumberMap(numbers)

// And initialize from pairs
const japaneseNumbers = new NumberMap([ ["一",1], ["二",2], ["三",3] ])

// Or something a bit smarter

const FriendScores = new MooseJS.TypedMap({
    is: "rw",      // Can be changed after initialization
    value: Number, // Values must be numbers
    key: String,   // Keys must be strings
    trigger: (map, newValue, oldValue, key) => {
        if (map.values().reduce((l,r) => l+r) > 20){
            throw Error("That's too many friends")
        }
    }
})

Enumerators

const TextEditor = MooseJS.defineEnum([ "vim", "atom", "nano" ])

new TextEditor("vim") // Fine
new TextEditor("emacs") // Err. Invalid input

Serialize

MooseJS objects can be described with the serialize function.

This is designed for creating self-describing interfaces.

Note that this will check required if required: true is set and the property has no default.


MooseJS.serialize(Student)

# Outputs:
{
  type: 'class',
  name: 'Student',
  properties: {
    name: { isa: 'String', required: true },
    dob: { isa: 'Date', required: true },
    grades: {
      isa: {
        type: 'array',
        name: 'TypedArray<Number>',
        data_type: 'Number'
      },
      required: false
    }
  }
}

Methods

MooseJS methods give you a way of guaranteeing the input (and output) types of a function.


const multiply_by_four = MooseJS.method({
	input: Number,
	output: Number,
	body: (input) => input *4,
})

// The following will throw a cast error!
multiply_by_four("Wait, I'm not a number")

Methods can also be used in classes.

const { method, defineClass } = require('moosejs')

class Student extends defineClass({
    final: true,
    has: {
        name:   { is:"ro", isa:String,   required:true },
        grades: { is:"rw", isa:[Number], required:true, default:[] },
        school: { is:"rw", isa:School,   enumerable:false },
    }
})
{
	getGradesAbove = method({
		input: Number,
		output: [Number],
		body: function(input){
			return this.grades.filter((grade) => grade >= input)
		}
	})
}

Asynchronous functions work too. The types will be checked after the promise is resolved.


const call_endpoint = MooseJS.method({
	input: URL,
	output: {
		code: { isa: Number, default:200 },
		body: { isa: String, default:"" },
	},
	body: async (url) => {
		await some_fancy_request_function("GET", url)
	}
})

call_endpoint("npmjs.com")
	.then(({ body }) => console.log(body))