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

niwa

v0.8.6

Published

NIWA intertwined web applications

Downloads

9

Readme

NIWA

NIWA Intertwined Web Applications

NIWA is an application server for simple NodeJs apps that can communicate with each other.

##Usage

  • maual
    • git clone or download
    • npm update or npm install
    • npm run start
  • cli
    	npm install -g niwa
    	niwa <work directory> <port>
  • api
    	let starter=require("niwa"); // function(niwaWorkDir=__dirname,overridePort=null)
    	starter();

#Work directory If no directory is provided the install directory is used. Config, Logs and application data is stored in this directory.

##structure

.
├─ config
   ├─ server.json
   ├─ apps.json
   ├─ users.json
   ├─ permissions.json
   └─ <appName>.config.json
├─ apps
   └─ ...
├─ logs
   └─ ...
└─ <work> // applications should persist files under a directory with its own name

You can use an empty directory as a work directory. In order to start without providing a port you need a config/server.json.

###config/server.json

{
	"port":8081,
	"logLevel":"INFO",			// default logger level
	"allowCommunication":true,	// default for communications
	"autoStart":false			// default to start app on server start
}

#####defaults

logLevel:"INFO"
allowCommunication:true
autoStart:false

#Apps An Application is a directory full of static assets with few exceptions. Each app is deployed in a separate NodeJs process (using Morgas nodeWorker). To deploy an app you can place it in a directory under apps or define it in the config/apps.json (or both).

##config/apps.json

{
	"<name>":{
		"path":"<relative from apps or absolute>",
		//overwrite defaults
		"autoStart":boolean,
		"logLevel":"<level>", // bunyan log levels
		"communication": boolean || {
			"<name>":boolean || ["<context>"]
		}
	}
}

##styles *.less files are automatically compiled when they are requested with a url containing /css/ instead of /less/.

e.g.: app/some/css/path/styles.less=>app/some/less/path/styles.less //compiled

##rest services Defining a rest service means simply putting a <name>.js file in the app/rest directory in your application. Those files are loaded when the application gets deployed. Such a file can export a function or a object structure of functions. Each context of the requested url is matched to a key in that structure until a function is found. The first context matches to the filename (without the extension). The remainder of the path as an array is also provided as the second argument of the function.

module.exports=function({method,headers,query,data,path},path=[])
{
	return "hello world";
}

A function may return any serializable (JSON.stringify) value or an instance of ServiceResult.

###context notes

  • running as a Morgas worker
  • process.cwd() // application directory
  • niwa/util is added as resource folder
    • ServiceResult
    • configManager
    • dependencyManager
    • mimeTypes (irrelevant)
  • global worker extensions

#####util/ServiceResult A small class for rest responses.

let ServiceResult=µ.getModule("ServiceResult");
return new ServiceResult({status:403,data:"Please log in first"});

#####util/configManager Creates a rest api for a persistent config. The config is saved under <work directory>/config/<app context>.json with file rotation of 3.

Parameter Morgas.Config or a config description (see Morgas.Config.parse)

returns

api=function(param) // rest api
api.ready //Promise that is resolves to Morgas.Conig instance
api.addChangeListener=function(path,fn)
api.removeChangeListener=function(pathOfFn,fn)
api.notify=function(path, oldValue, newValue) // triggers changeListeners
api.save=function() // returns Promise

Example service

module.exports=µ.getModule("configManager")({
	name:"string",
	range:{
		type:number,
		default:3,
		min:0,
		max:10,
		step:0.5
	},
	check:{
		type:"boolean"
	},
	list:{
		type:"array",
		model:"string"
	}
});

#####dependencyManager Creates a rest api to serve js files with all needed dependencies. It uses morgas/lib/dependencyParser to parse files

Parameter Array of file or directory paths and a base path from which navigation begins

returns

api=function(param) // rest api
api.addResource=function(moduleRegister,moduleDependencies,directory,name=null)

Morgas and MorgasGui resouces are already registered

Example service

module.exports=µ.getModule("dependencyManager")(["js"],"js");

####global worker extensions

worker.eventSource=function(name,getter) returns trigger function(eventName,data) for server send events
worker.module=function(moduleName,methodName,args=[]) // call module
worker.getCommunicationList=function(name) // returns a Promise resolving to an array of contexts
{
	return worker.ask("NIWA","communicationList",{name:name});
};
worker.ask=function(context,method,data) // communicate with (call methods from) other applications 
worker.serverStarted=function() // overwrite for callback when server is started and communication is possible

###package.json Having a package.json for an application is optional. But it is needed for communication and additional configuration.

#####application name The name of the application is defined in niwa.name or name of its package.json

####setup script If you define a niwa.setup the script gets called (required) when starting the application. It is possible to export a Promise for asynchronous setups.

The script is called before any rest service is loaded.

#####dependencies You can define application dependencies in the niwa.dependencies array. Currently this is only a treated as a hint and is therefore optional.

##Communication Applications with a name can communicate with each other. It has to be allowed in the apps.json.

#####Example app1

worker.myMethod=function(data,context)
{
	if(context=="someContext") return Promise.reject();
	return "value";
};

app2

worker.getCommunicationList("app1")
.then(function(list)
{
	if(list.length==0) return Promise.reject();
	
	return worker.ask(list[0],"myMethod")
	.then(function(data)
	{
		//handle answer
	});
}

#Modules Modules are extensions that can be called from applications.

##session Session object

{
	"token":"token",
	"user":{
		"name":"name"
	}
}

Guest users have always an empty string as name

####methods

  • create() => Session object
  • get(token) => Session object
  • getOrCreate(token) => Session object
  • delete(token) => boolean
  • refresh(token) => boolean // mostly internal

##user ####methods

  • logIn(sessionToken,username,password="") => void (reject value is a ServiceResult)
  • logOut(sessionToken) => void (reject value is a ServiceResult)
  • register(sessionToken,username,password) => void (permission "registerUser" needed)
  • delete(sessionToken,username) => void (permission "deleteUser" needed)
  • list(sessionToken) => void (permission "readPermissions" needed)

##permissions ####methods

  • check(sessionToken,toCheck=[]) => void (reject value is a ServiceResult)
  • checkAll(sessionToken,toCheck=[]) => Boolean[] (reject value is a ServiceResult)
  • getAll(sessionToken) => void (permission "readPermissions" needed)
  • addUser(sessionToken,name) => void (permission "addUser" needed)
  • setUser(sessionToken,name,roles,permissions) => void (permission "setUser" needed)
  • deleteUser(sessionToken,username) => void (permission "deleteUser" needed)
  • addRole(sessionToken,name) => void (permission "addRole" needed)
  • setRole(sessionToken,name,roles,permissions) => void (permission "setRole" needed)
  • deleteRole(sessionToken,name) => void (permission "deleteRole" needed)

#Server send events EventSources can be accessed via <app context>/event/<event name>. Ordinary get requests are also supported. ###usage

  • app
    1. let notify=worker.eventSource("myEvent",()=>"init value");
    2. notify("update",value);
  • client
    1. let source=new EventSource("event/myEvent");
    2. source.addEventListener("init",event=>alert(event.data));
    3. source.addEventListener("update",event=>alert(event.data));

#Special paths host:port/morgas/* serves Morgas files host:port/morgas/gui/* serves MorgasGui files host:port/morgas/gui/css/theme/<theme name> serves MorgasGui compiled theme style host:port/morgas/gui/css/<module name>[/<theme name>] serves MorgasGui compiled module styles