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

webend

v0.1.3

Published

Web App wide Plug-in System

Downloads

3

Readme

Build Status Test Coverage

Everything is a Plugin

Webend is a web wide plugin system. A feature often consists of frontend and backend part. Combine both and develop each feature as plugin to keep it easily, maintainable und testable. Support easily different product lines for different customers.

Installation

npm i --save webend

The system is devided into two parts: webend & webend_hub. The webend will load the NodeJS backend plugins while the webend_hub will connect all frontend plugins. To support front- and backend you have to install both

Setup

Just import the webend package.

import 'webend';  //require('webend');

Two config files have been copied to your project root directory while the installation. The config.json is the configuration for the system and the pluginconfig.json the configuration for all plugins.

Before we can start the system we have to configure the path for a secret wich will be used for the encryption of the control page. Therefor edit the config.json. Change privateKeyPath to your local private key. (The file could contain just a string.) There you can also change the port of the control page.

The users that are allowed to login to the control app have to be defined. Therefore create a users.json in your project root which contains the username as key and the password as value. If this file doesn't exists then the default user admin:admin will be used.

Now you can start your module!

Control App

Open localhost:8080 in the browser and login with your defined user. The app should be self explaining. Activate the autostart of webend_hub.

Write a PLUGIN

1) Instantiate a new npm package

A plugin is always a npm package. Create a new one with: npm init

The plugin name always has to have webend_ as prefix!

2) NodeJS Backend Part

The entry point to your backend has to be declared in the main field of the package.json. The given entry file has to export a function(es5) or per default a class(es6/typescript). The constructor will be fired when the plugin will be loaded. So you can put your starting code there. Sometimes some initialisations on the class needs to be done. This could be done also in the constructor and you can move your starting code to a new start method. This start method is optional and will be fired after the class has been created. The given entry will be loaded by an dependency injection framework. Webend uses tiny-di.

There are the following Injectables available:

Name | Type | Description | Example | --- | --- | --- | --- | injector | tiny-di | The Injector can be used to bind additional instances to the injector or get instances from the injector. Further methods of the injector can be found in the tiny-di documentation. | injector.get('status') | status | object | Gets the loading status of the different plugins | status['webend_hub'] -> true | autostart | object | Gets the autostart setting of the plugins | autostart['webend_hub'] -> true | config | object | contains the configs for all plugins | config['webend_hub'] -> {port: 8080} | {pluginName} | object | Gets the instance of an other plugin which has to be a dependency of the current plugin | n.a. |

Thats a lot of theory so here is an example:

import * as express from 'express';
import TinyDiInjectable from './tinydiinjectable';

export default class Server extends TinyDiInjectable {
  constructor(_config: any, injector: TinyDiInjector) {
    let config = _config['webend_server'];
    super();
    
    let app = express();
    
    app.get('/', (req, res) => {
      res.send('Hello World');
    });
    
    injector.bind('webend_server_app').to(app);
    
    let server = app.listen(config.port);
    console.log('started on ' + config.port);
  }
}
Server.$inject = {
  deps: ['config', 'injector'],
  callAs: 'class'
};

We create an plugin which creates an express server and makes the app variable available for other plugins. To inject dependencies we have to declare the $inject property on the class. Therfore we need to inherit that property from the TinyDiInjectable class. In es5 you don´t need the TinyDiInjectable class. Its only for declaration purposes.

The port should be configurable through the Control App. Therefore we change the package.json to:

{
  "name": "webend_server",
  "version": "0.0.1",
  "main": "server.js",
  "dependencies": {
    "express": "^4.13.4"
  },
  "webendConfig": {
    "port": "3000"
  }
}

The configurations can be injected. We only want the config of you plugin so we use _config['webend_server'] only. We want to export the express instance that other plugins can define other routes on it. Thats why we have injected the injector to bind it to webend_server_app with injector.bind('webend_server_app').to(app);. The name should always contain the current plugin name that there are no naming conflicts between different plugins.

3) Frontend Part

The frontend part can be a collection(object) of Angular 2 components, directives, services or an AngularJS Module. The following browser field has to be added to the package.json:

{
  "name": "webend_mypage",
  "browser": {
    "entry": "main",
    "framework": "angular2" //or "angular"
  }
}

The entry defines the entry module and should not contain the js extension of the file. Only the filename is needed. As frontend frameworks are currently only angular and angular2 supported. A plugin can only contain code of one framework but plugins of different frameworks can be combined and the ressources can be shared between different frameworks!

At first we need to connect one (or mulptiple) directives to the document body. Therefore we got two different opportunities. The automatic modus is the default one. There we have to create a directive which has the plugin name as selector. All these directives will be automatically added to the document body. The second one is the more configurable modus. There you have to edit the configuration of the webend_hub. Therefore open the controll app, navigate to Management and click on the configure button of the webend_hub. There you can set the useCustomIndexHtml to true and edit the index.html. It is fully customizable and you can add your root directive into the body.

Now we want to use ressources of other plugins. We start with an Angular 2 plugin and want to use components, directives and services of another Angular 2 plugin.

Angular 2

For example we implement the plugin webend_mypage. The requested ressources are defined in another plugin which is a optional dependency of webend_mypage. Its optional because will also want be able to load the plugin without the additional. The entry file is main.ts which looks like:

import { Component, Inject } from '@angular/core';
declare var webend: any;
let MyPageService = webend.getService('MyPageService');

@Component({
  moduleId: module.id,
  selector: 'webend_mypage',
  template: '<span my-highlight>this my page!!!</span> <my-addon></my-addon> {{secret}}',
  directives: [webend.getComponent('my-addon'), webend.getDirective('[my-highlight]')],
  providers: [MyPageService]
})
export class webend_mypageComponent {
  public secret: string;
  constructor(@Inject(MyPageService) myService: any) {
    this.secret = myService.secret;
  }
}

The selector for the component is equal to the plugin name so the component will be attached to the document body. To use components or directives in your template we have to push them into the directives array. We can't require them because we don't know if the plugin is avialable. But we can request a component through webend.getComponent. This wil lreturn the requested component iff available and otherwise it will return a component with empty template. So this will never fail and we can use the component in our template. The same could be done for a directive. The input of the functions are the selectors as string.

Nearly the same is also available services. Angular 2 has a dependency tree so you can inject thorugh the providers array. They will automatically added to the root injector that they are available for all plugins.

AngularJS ressources can also be used. Therefore is an adapter defined on the webend: webend.adapter.* . Have a look to ngAdapter for usage. Not all AngularJS directives can currently be upgraded! To be able to use optional AngularJS ressources use getOptNg1Directive or getOptNg1Service. Example:

webend.adapter.upgradeNg1Directive(webend.getOptNg1Directive('myDirective'));

AngularJS

Lets have a look to an AngularJS plugin (webend_myng1page):

let mod = angular.module('webend_myng1page', [])
  .directive('webendMyng1page', MyNg1Page);

declare var webend: any;

ctrl.$inject = [webend.getOptNg1Service('MyPageService'), '$scope'];
function ctrl(MyPageService: any, $scope: any) {
  $scope.secret = MyPageService.secret;
}

function MyNg1Page() {
  return {
    template: `
      <div>
        Hello from the old AngularJS World!
        {{secret}}
        <div red-light>This is a Highlight!</div>
      </div>
    `,
    controller: ctrl
  };
}

export default mod;

We have to create an AngularJS module with the name of the plugin. The controller of the added directive wants to use a optional AngularJS Service. Therfore the Service has be requested thorugh webend.getOptNg1Service. Thereby it will be secured that the service is always available (but could be empty). The directive uses the redLight directive of another plugin. This could be defined or not. AngularJS ingores unknown directives so there is no need to secure that the directive exists.

It is possible to use Angular2 components, directives and services in an AngularJS plugin. Therefore use the webend.adapter to downgrade the specific ressource. Have a look to ngAdapter for documentation.

Dependencies

We always talked about optional Dependencies. Thats a new case which I want to be highlgihted. If you need plugins as dependency its possible to request them the described way. I would recommend that but it is also possible to use them in the standard native way.

Summary: additional functions on webend

Name | Return Value | Description --- | --- | --- getComponent | Component(Angular2) | Returns the requested component iff available otherwise a component with empty template. getDirective | Directive(Angular2) | Returns the requested directive iff available otherwise a directive which does nothing. getService | Service(Angular2) | Returns the requested service iff available otherwise an empty service. getOptNg1Directive | String | Initialises empty directive if not existent. Returns directive selector. getOptNg1Service | String | Initialises empty service if not existent. Returns name of service. adapter | ngAdapter | Use the ngAdapter to up and downgrade directives and services.