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

snooze

v1.0.0

Published

NodeJS Dependency Injection Framework

Downloads

67

Readme

Snooze 1.0.0-alpha.1

Differences to pre-alpha snooze

Pretty much everything has been reworked here. Snooze is no longer opinionated on how it should be used and does not focus on creating a RESTful server (though it can still be used to). It has removed most of what was in the pre-alpha version to create a better foundation for a more powerful and modular framework. Mostly this is done in the way of Entities. More information provided in the README below.

Installation

# install snooze to your local project
npm install snooze

# install snooze-baselib as a starting place for your project
npm install snooze-baselib

# optionally install snooze-cli to start
# your projects using a snooze.json config file
sudo npm install -g snooze-cli

Setup

To create a simple one-file snooze application an example is shown below. More advanced setups later in the README. It is recommended that beginners, intermediates, and advanced users use the snooze-baselib module unless there are specific reasons not to. The snooze-baselib module contains usefule Entities and importProcesses.

// main.js
(function() {
    'use strict';
    
    // Snooze is a global object. Because of node's require caching
    // the snooze object has one instance accross all files.
    var snooze = require('snooze');
    
    // Creates a myApp module and imports the `snooze-baselib` module
    snooze.module('myApp', ['snooze-baselib'])
        // Creates a run function that will be ran when the application starts
        .run(function() {
            console.log('Hello World!');
        })
        // Does some compiling and begins the app by calling all config
        // and run processes.
        .wakeup();
})();

Outputs

# running via terminal
node main.js
Hello World!

Modules

Modules are a flexible wrapper or foundation for your project. They can be designed to share a set of functionality to other modules (and are not intended to run alone), or they can be designed to run your application. Modules greatest strength is that it allows sharing functionality easily and in an abstrated, modular way. To create a module use the snooze.module method.

var snooze = require('snooze');
snooze.module('myApp', []);

When creating a module the second param is required. It can be an empty array or an array of modules to import. All module methods that don't return a value returns itself making commands chainable.

snooze.module('myApp', []).doSomething().doSomethingElse();

If the modules is already created you can access it be omitting the array.

snooze.module('myApp').doSomething();

Importing Modules

Importing modules lets you import functionality into your application seemlessly. Modules can be written yourself or downloaded using npm. snooze-baselib is a highly recommended starting point. Using it as an example, to install it and import it into your project do the following.

 npm install snooze-baselib --save
snooze.module('myApp', ['snooze-baselib']);

If the npm module hasn't been required using require() snooze will try to do this automatically. If you are trying to import a local module (not from npm) require() it manually before you try to import it into another module.

Note When a module is constructed the dependant modules will import immediately.

run

A run processes may be what you use to run your app. Your module can define multiple run processes that will get called once wakeup or doRuns is called. It's recommended you should use wakeup instead. Run processes allow injected Entities.

snooze.module('myApp').run(function(MyService) {
    MyService.doSomething();
});

config

Config processes are called before run processes. Entities may define a config object. This is up to the writer of the entity to provide as well as document.

snooze.module('myApp').config(function(MyService) {
    MyService.maxSomethings = 10;
}).run(function(MyService) {
    MyService.doSomething();
});

Depending on how the Entity is written. Some entities may provide a config and an injectable, just a config, or just an injectable. See the documentation of the Entity for it's possible uses.

wakeup

Because I am a pun-master (i'm not), to start your snooze application you should wakeup. This will go through the configPreprocessors, config processes, and run processes in that order.

snooze.module('myApp')
    .run(function() {
        console.log('foo');
    });

console.log('bar');

snooze.module('myApp').wakeup();

Outputs

bar
foo

Entities

A Service in pre-alpha is what is now a type of Entity in alpha. An Entity can be created and customized to serve any purpose. Using the recommended snooze-baselib module in your projects you get the service, value, and constant entities in your project. Other entities can be imported from other modules or created by the developer and shared through imports.

An Entity can be injected into snooze processes or any "injectable function". They can also be injected into Entities that define a function for their constructor. Taking the service Entity from snooze-baselib we can create a Math service for our application.

// lib/services/Math.js
(function() {
    'use strict';
    var snooze = require('snooze');
    
    // Constructing a service named Math. The second parameter is the constructor.
    // This function can also contain injectable Entities like services.
    snooze.module('myApp').service('Math', function() {
        return {
            sum: function(num1, num2) {
                return num1, num2;
            }
        };
    })
    .run(function(Math) {
        console.log(Math.sum(10, 5));
    });
});

Outputs

node main.js
15

Note we don't include the dependencies here. When dependencies are defined, snooze will construct a module. When no dependencies are defined you are accessing the module.

Entities come in different shapes and sizes. Lets create a bank app that prevents you from withdrawing more that $500 at a time.

// lib/services/Bank.js (function() { 'use strict'; var snooze = require('snooze');

    snooze.module('myApp').service('Bank', function(maxWithdrawAmount) {
        return {
            withdraw: function(accountBalance, withdrawAmount) {
                if(withdrawAmount > maxWithdrawAmount) {
                    console.log('Unable to withdraw $' + withdrawAmount + ' as it exceeds the max withdraw amount of $' + maxWithdrawAmount);
                    
                    return accountBalance;
                } else {
                    return accountBalance - withdrawAmount;
                }
            }
        };
    })
    .constant('maxWithdrawAmount', 500)
    .run(function(Bank) {
        var myBalance = 1000;
        myBalance = Bank.withdraw(myBalance, 700);
        myBalance = Bank.withdraw(myBalance, 200);
        
        console.log('$' + myBalance);
    });
});

Output

node main.js
Unable to withdraw $700 as it exceeds the max withdraw amount of $500
$800

For more information on service, value, and constant, see the snooze-baselib README.

Registering Entities

Each module is responsible for importing it's own entities. You don't need to worry about importing the entities from a module you are importing. The module you are importing (say snooze-baselib) will import it's Entity types (service, value, and constant) as well as entities built on these types (like in our Math example). To create your own entities on these types and import them use the registerEntitiesFromPath method. You can specify the exact path of the file to import or use a globbing pattern.

// main.js
(function() {
    'use strict';
    var snooze = require('snooze');
    
    snooze.module('myApp', ['snooze-baselib'])
        .registerEntitiesFromPath('lib/services/Math.js')
        // .registerEntitiesFromPath('lib/services/*.js') [all js files in lib/services]
        // .registerEntitiesFromPath('lib/**/*.js') [all js files in lib recursively]
        .run(function(Math) {
            console.log(Math.sum(10, 5));
        })
        .wakeup();
})();

snooze.json (config)

If the root of your app has a snooze.json file in it, snooze will load the contents into the config when it's constructed. Modules can be written to read the config and change the behavior of your application in the configuration or running phases of your application. The only available property in vanila snooze.json is the silent property. False be default but set to true and logs and warns will not print to the console.

{
    "silent": true
}

Advanced Modifications

In this section I'll go into creating custom Entities, Config Preprocessors, and Import Processes, and extending snooze.json.

Before you do this consider the following.

  • Is this functionality available elsewhere?
  • If this functionality exists but doesn't provide exactly what I need should I try contacting the author first?
  • How can this affect other modules?
  • Is it modular enough?
  • Is it abstracted?
  • Am I modifying basic functionality accross the app in a potentially harmful way?
    • Did I document this well enough?

In several ways you can change the way snooze and modules work entirely through these features. This flexibility is powerful but can also be harmful. Be careful, be nice.

Creating Entities

Entities is a loose term for the type of Entity as well as instances of that type. An Entity type is refered to as an EntityGroup. And instance of an EntityGroup is an Entity. An Entity also creates and instance of itself when it's injected. So we have EntityGroup, Entity, and EntityInstance. Using snooze-baselib again as an example, a service is an EntityGroup. If we create a service called Math, Math is an Entity. When Math is injected (in say, a run processes) it injects an instance of Math called an EntityInstance.

EntityGroups

To create the service EntityGroup we will do the following.

// lib/entities/service.js
(function() {
	'use strict';

	var snooze = require('snooze');

	var Service = new snooze.EntityGroup();
	Service.type = 'service';

	module.exports = Service;
})();

The name, or type, of the Entity should set to the new EntityGroup object constructed from snooze.EntityGroup. The type should not contain spaces. The reason for this is because when this EntityGroup is compiled it's type will become a method on the module.

Before EntityGroup

snooze.module('myApp')
    .service('MyService', function() {}); // Error: undefined is not a function

After EntityGroup

snooze.module('myApp')
    .registerEntityGroupsFromPath('lib/entities/*.js')
    .service('MyService', function() {}); // All good
    
Compiling

The EntityGroup has a set of methods that take an Entity and apply it's config, injection, etc. An Entity is constructed in 2 parts. The first being the name of the Entity, the second being the constructor. The constructor in the above examples is the function, but this can be any value you defined as the constructor. In the snooze-baselib constant Entity the constructor is not a function but just whatever value is passed in the second argument as is. In any case, we need to create a compile method that takes the constructor and constructs an instance. Entities compile when module.EntityManager.compile or module.wakeup is called. They are not compiled when registered because some Entities will be defined with dependencies that don't yet exist. Once wakeup is called an module.isAwake is set to true, Entites will autocompile individually.

Service.compile = function(entity, entityManager) {
	entity.instance = entityManager.run(entity.constructor);

	if(entity.instance.$compile) {
		entity.instance.$compile();
	}
};

The service compile takes the constructor and runs it as an injectable function using EntityManager. The returned value is the EntityInstance. Additionally, if the newly created instance has a $compile method it will run that after compiling the instance.

Registering Dependencies

Once the instance has been compiled we should write the registerDependencies method. Not all Entities will have dependencies and the ones that do may not use an injectable function as it's constructor. Because of this, it's up to the author of the Entity to create a method that will register the Entities dependencies. This step is important, it will prevent circular dependencies and infinite loops.

Service.registerDependencies = function(entity, entityManager) {
	if(typeof entity.constructor === 'function') {
		entity.dependencies = snooze.Util.getParams(entity.constructor);
	} else {
		throw Error('Services expect function constructors. ' + (typeof entity.constructor) + ' given');
	}
};
Injecting

The EntityGroup should define what an Entity provides when it's being injected. In the case of a service we will return the instance unless $get is defined (in which case that will be returned).

Service.getInject = function(entity, entityManager) {
	if(entity.instance.$get) {
		return entity.instance.$get;
	}

	return entity.instance;
};

Using $get

snooze.module('myApp')
    .service('MyService1', function() {
        // Here is the injected value
        return {
            foo: function() {
                return 'bar';
            }
        };
    })
    .service('MyService2', function() {
        var properties = {
            fooValue: 'bar'
        };
        return {
            properties: properties
            // Here is the injected value
            '$get': {
                foo: function() {
                    return properties.fooValue
                }
            }
        };
    })
    .run(function(MyService1, MyService2) {
        console.log(MyService1.foo());
        console.log(MyService2.foo());
    });

Outputs

bar
bar
Configuring

Similar to the getInject method but used when injecting into module.config processes.

Service.getConfig = function(entity, entityManager) {
	if(entity.instance.$config) {
		return entity.instance.$config;
	}

	return entity.instance;
};

Using to update $get.

snooze.module('myApp')
    .service('MyService1', function() {
        // Here is the injected value
        return {
            foo: function() {
                return 'bar';
            }
        };
    })
    .service('MyService2', function() {
        var properties = {
            fooValue: 'bar'
        };
        return {
            properties: properties
            // Here is the injected value
            '$get': {
                foo: function() {
                    return properties.fooValue
                }
            }
        };
    })
    .config(function(MyService2) {
        MyService2.properties.fooValue = 'baz';
    })
    .run(function(MyService1, MyService2) {
        console.log(MyService1.foo());
        console.log(MyService2.foo());
    });

Outputs

bar
baz

Post Compile

Entity instances can call a post-compile process. This is useful for managing services that need to do their own level of compiling once it's dependencies are ready. To set the post-compile processes set the $post value on the instance return object.

snooze.module('myApp')
    .service('ServiceManager', function($entityManager) {
        var services = [];

        // This will run after EntityManager.compile has finished compiling all Entities.
        function $post() {
            var srvs = $entityManager.getEntities('service');
            for(var i = 0; i < srvs.length; i++) {
                services.push(srvs[i].instance);
            }
        };

        function getServices() {
            return services;
        };

        return {
            '$get': {
                getServices: getServices
            },
            '$post': $post
        };
    });
Other Properties

private - You can set an Entity as private. This means it will not be shared to an importing module. (default: false)

Service.private = false;

injectable - You can set any individual Entity as injectable or not. If false, the Entity cannot be injected into other Entities or run processes. (default: true)

Service.injectable = true;

configurable - You can set any individual Entity as configurable or not. If false, the Entity cannot be injected into config processes. (default: true)

    Service.compile = function(entity, entityManager) {
		entity.instance = entityManager.run(entity.constructor);

		entity.private = entity.$private || entity.private;
		entity.injectable = entity.$injectable || entity.injectable;
		entity.configurable = entity.$configurable || entity.configurable;

		if(entity.instance.$compile) {
			entity.instance.$compile();
		}
	};
    // This service is only configurable
    snooze.module('myApp')
        .service('routeManager', function() {
            return {
                $injectable: false,
                path: function(url, cb) { ... }
            };
        })
        .config(function(routeManager) {
            routeManager.path('/users', function() { ... });
        });

constant - You can set an EntityGroup as constant (true) to throw an error if the Entity is attempted to be overwritten. (default: false)

Service.constant = false;

Import Processes

Import processes define how to import modules into others. By default, snooze has no import processes. snooze-baselib defines processes to import Entities. An import process is a function that returns the import process function. The first function allows manipulation of the existing importProcesses. The second (nested) function gets appended to the array of import processes.

The import processes is given the source (imported) module and the dest (importee) module.

Here is the processes for importing Entities.

    // lib/importProcesses/importEntities.js
    (function() {
    	'use strict';
    
    	module.exports = function(processes) {
    		return function(source, dest) {
    			var entities = source.EntityManager.getEntities();
    
    			for(var i = 0; i < entities.length; i++) {
    				var entity = entities[i];
    				dest.log(('+ Entity: ' + entity.getName()).blue);
    				if(dest.EntityManager.entityExists(entity)) {
    					dest.warn('Entity Exists: ' + entity.getName());
    				} else {
    					dest.EntityManager.registerEntity(entity);
    				}
    			}
    		};
    	};
    })();
snooze.module('myApp')
    .registerImportProcessesFromPath('lib/importProcesses/*.js');

Config Preprocessors

Config preprocessors is another tool to help you extend snooze.json. snooze-baselib defines a preprocessors that allows you to define run modes for your application.

    // lib/configPreprocessors/mergeModeConfig.js
    (function() {
    	'use strict';
    
    	module.exports = function(processes) {
    		return function(config, module) {
    			var mode = config.mode;
    			if(mode) {
    				if(config.modes[mode]) {
    					for(var key in config.modes[mode]) {
    						config[key] = config.modes[mode][key];
    					}
    				}
    			}
    
    			console.log(config);
    		};
    	};
    })();
snooze.module('myApp')
    .registerConfigPreprocessorsFromPath('lib/importProcesses/*.js');

Extending snooze.json

Unrecognized properties in the snooze.json are passively ignored. Using configPreprocessors and writing your Entities to read from the config lets you define custom configurations. The entire configuration is always available when called. One example for extending the snooze.json is if you were creating an HTTP service.

    // snooze.json
    {
        "mode": "development",
        "modes": {
            "development": {
                "port": 8080
            },
            "production": {
                "port": 80
            }
        }
    }
    snooze.module('myApp')
            .service('HTTP', function($config) {
                var port = $config.port;
                
                // ...
            });

What properties are available in config should be documented.