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

@mobiusz/assemblage

v0.2.0-alpha.8

Published

Möb:usz framework's assemblage module.

Downloads

1

Readme

@mobiusz/assemblage

lerna made

Donate

paypal

Description

@mobiusz/assemblage contains the Möb:üsz framework's plugins creator and manager.

It proposes an API to create Assemblages, that allow the whole system to quickly integrate new development and tools, including at runtime.

Assemblages are managed modules that make available all the particular elements they need to be integrated in a Möb:üsz ecosystem, both server-side and client-side. For example, @mobiusz/user exposes both the User Manager assemblage, that allows to manage, create, delete users in tthe database, and the GUI components to visualize and manipulate the system. When integrating the assemblage, it's up to the final developper to decide how and when to display the components, imported from the @mobiusz/user package itself.

@mobiusz/assemblage exposes a singleton named Mobiusz to manage all installed assemblages.

One advantage of this pattern is that it avoids duplication of handlers and that everything can be cleanly disposed when calling M.destroy() or when the application quits unexpectedly.

Targets: node, browser, electron.

Warning

This module is still a work in progress, and API or implementations can change drastically from one version to another. Use at your own risks!

Install

With yarn (recommended)

yarn add @mobiusz/assemblage

With npm

npm install @mobiusz/assemblage

Usage

Use an Assemblage

To use an assemblage, you have to install and import it where you'd like to use it, e.g. at the main entry point of your application.

yarn add @mobiusz/user

import { Mobiusz as M } from '@mobiusz/assemblage';

// DO THIS...

import { AUser } from '@mobiusz/user';

M.register(AUser) // Registers only this assemblage.
const userManager = new AUser();
await M.use(userManager, options);

// OR THIS... 

M.register('@mobiusz/user'); // i.e. in your main entry point, registerrs all exported assemblages.

// Later, instantiate anywhere the assemblages exported by the package.

import { AUser, AAnotherAssemblageExported } from '@mobiusz/user';

const userManager = new AUser();
await M.use(userManager, options);

@mobiusz/assemblage is at its very first step. The roadmap contains the ability to instantiate assemblages automatically - as they tend to be singletons - and get them with the already existing method M.require('com.example.my-assemblage-name');

As of July, 2022, you can get the assemblage already registered and instantiated elsewhere, by using this method.

const userManager = M.require('io.benoitlahoz.user');

userManager.create(/* options for the user to be created... */);

Messages

Mobiusz singleton object extends @mobiusz/mediator main object. As its name suggest it's a mediator (an event handler) between registered assemblages. Other assemblages and client can then listen to messages that are exposed by any registered assemblage, or by Mobiusz itself.

Mobiusz emits these messages:

  • AssemblageRegistered: When an assemblage is registered by the manager,
  • AssemblageInitialized: When an assemblage has been initialized,
  • AssemblageDisposed: When an assemblage has been destroyed,
  • AssemblageUnregistered: When an assemblage has been unregistered from the manager.
Example
M.on(AssemblageMessages.AssemblageRegistered, ({ name: string, pkgName: string, }) => {

  // i.e.: name = 'io.benoitlahoz.user' / pkgName = '@mobiusz/user'

})

User can easily trap messages touse them with another module (note that user is responsible for security):

M.on('*', (args:any[]) => {
  // Do something with the event (i.e. pass it to socket.io)
})

Create an Assemblage

A custom library can export one or more assemblages and its objects.

Decorator

The 'assemblage factory' consists a typescript decorator that give all information about the assemblage. Then the factory creates a Proxy to be used by the manager or elsewhere.

The entity for the Assemblage decorator is the following:

export interface AssemblageStaticDescriptionModel {
  pkgName: PackageName; // @mobiusz/user
  name: AssemblageName; // 'io.benoitlahoz.user'
  target?: 'node' | 'browser'; // or undefined
  events?: MessageStringList; // An enum of messages to be registered for this assemblage.
  dependencies?: PackageName[]; // A list of packages names the assemblage is dependent on.
  components?: Record< // A list of package.json entry points for components, with their name and description, for runtime import.
    PackageEntryPath,
    {
      name: NameString;
      description: string;
    }[]
  >;
}
Example
export enum UserMessages {
  // Creation.
  UserCreate = 'user:create',
  UserCreated = 'user:created',
  // Invitation.
  UserCheckInvitation = 'user:check.invitation',
  UserRequestInvitation = 'user:request.invitation',
  UserCheckRequest = 'user:check.request',
  UserPasswordFromInvitation = 'user:password.from.invitation',

  // Credentials.
  UserLogin = 'user:login',
  UserCheckToken = 'user:check.token',
  UserChangePassword = 'user:change.password',

  // Connection.
  UserAuthenticated = 'user:authenticated',

  // Getters.
  UserGetAllButMe = 'user:get.all.but.me',
  UserGetAll = 'user:get.all',
  UserGet = 'user:get',
}

@Assemblage({
  name: 'io.benoitlahoz.user',
  pkgName: '@mobiusz/user',
  target: 'node',
  events: UserMessages,
  dependencies: ['@mobiusz/db', '@mobiusz/mailer'],
})
export default class extends ManagedDocument implements AssemblageModel {
  private _eventBus: any;

  constructor() {
    // This assemblage extends '@mobiusz/db' ManagedDocument class.
    super();
  }

  public onRegister(): void {
    // Nothing to do here for this assemblage. Hook is not mandatory.
  }

  // eventBus is an object passed by Mobiusz manager to communicate with other assemblages.
  public onBeforeInit({ eventBus }, options?: any) { 
    this._eventBus = eventBus;

    // ... omitted for brievity.

    // Listens to manager emitting one of our assemblage's message.
    this._eventBus.on(
      UserMessages.UserCreate,
      async (payload: UserCreateModel, callback?: Function) => {
        // ...
      }
    );

    // ...

  }

  public onDispose() {
    super.dispose();
  }

  public async create(description: UserCreateModel): Promise<any> {
    
    // ...

    return success;
  }

  // ...

}

// Then in the main entry point of the library, so we can change implementation at any time while keeping the same name.
export { default as AUser } from './assemblage'

Lifecycle Hooks

The assemblages hooks are called in this order.

  • onRegister: A static class hook called before instantiating an assemblage. It registers authorized messages.
  • onBeforeInit: Called before 'init' as its name suggest. Mobiusz passes the eventBus and eventual options to this hook.
  • onInit or onAsyncInit: Same as beforeInit but one step later.
  • onBeforeDispose: Application called M.dispose([assemblage]) on the assemblage or M.destroy(). Time to clean-up!
  • onDispose: Same as beforeDispose but one step later. After this call, assemblage will actually be disposed.
  • onUnregister: Unregister the assemblage and authorized messages.

Errors

  • UnregisteredAssemblageError
  • AssemblageInitializationError
  • NotAnAssemblageError

Tests

Tests are handled with Jest.

yarn test

Todo

  • Manage order of declared dependencies between assemblages to order registration (already in place thanks to the package toposortbut unused for the time being). (assemblage-manager.ts:231)
  • Call beforeInit for all assemblages before to call init.
  • Use M.require to get the assemblage's class instead of the instance, so we can 'use' it after call to require?
  • A CLI with a template to create an assemblage with everything in place (@mobiusz/cli).
  • Register and instantiate assemblages through a configuration file (everything in place yet).
  • Public unregister method.
  • Make assemblages singletons by default?

Contributors

Author

Benoît Lahoz

License

GPL-3.0+