alar
Auto-Load And Remote.
Last updated 7 days ago by hyurl .
MIT · Repository · Bugs · Original npm · Tarball · package.json
$ cnpm install alar 
SYNC missed versions from official npm registry.

Alar

Alar is a light-weight framework that provides applications the ability to auto-load and hot-reload modules, as well as the ability to serve instances remotely as RPC services.

Prerequisites

  • Node.js v8.3.0+

Auto-loading and Hot-reloading

In NodeJS (with CommonJS module solution), require and import will immediately load the corresponding module and make a reference "copy" in the current scope. Which means, if the module doesn't finish initiation, e.g. circular import, the application may not work as expected, and if the module file is modified, the application won't be able to reload that module without restart the program.

Alar, on the other hand, based on namespace and ES6 proxy, it creates a weak-reference of the module, and only import the module when needed. And since it's weak-referenced, it will not make any copy to the module, and when the module file is changed, it can wipe out the memory cache and reload the module with very few side-effects.

How to use?

In order to use Alar, one must create a root ModuleProxy instance, and assign it to the global namespace, so other files can directly use it as a root namespace without import and share the benefits of declaration merging (in TypeScript vernacular, if not using is, just ignore any tips and code of declaration merging that may be discussed).

Example

// src/app.ts
import { ModuleProxy } from "alar";

// Expose and merge the app as a namespace under the global namespace.
declare global {
    namespace app { }
}

// Create the instance.
export const App = global["app"] = new ModuleProxy("app", __dirname);

// Watch file changes and hot-reload modules.
App.watch();

In other files, just define and export a default class, and merge the type to the namespace app, so that another file can access it directly as namespace.

(NOTE: Alar offers first priority of the default export, if a module doesn't have default export, Alar will try to load the entire exports object instead.)

// src/bootstrap.ts
declare global {
    namespace app {
        const bootstrap: ModuleProxy<Bootstrap>
    }
}

export default class Bootstrap {
    init() {
        // ...
    }
}
// src/service/user.ts
// The namespace must correspond to the filename.
declare global {
    namespace app {
        namespace service {
            const user: ModuleProxy<User>
        }
    }
}

export default class User {
    constructor(private name: string) { }

    getName() {
        return this.name;
    }
}

And other files can access to the modules via the namespace:

// src/index.ts
import "./app";

// The instance() method will link to the singleton instance of the module.
app.bootstrap.instance().init();

// The create() method will create a new instance.
var user = app.service.user.create("Mr. Handsome");

console.log(user.getName()); // Mr. Handsome

Prototype Module

Any module that exports an object as default will be considered as a prototype module, when calling create() of that module, the object will be used as a prototype (since v4.0.4, a deep clone will be used instead, if an argument is passed, it will be merged to the new object). However when calling instance() of that module, the original object itself will be used as the singleton.

// src/config.ts
declare global {
    namespace app {
        const config: ModuleProxy<Config>;
    }
}

export interface Config {
    // ...
}

export default <Config>{
    // ...
}

Remote Service

Alar allows user to easily serve the module remotely, whether in another process or in another machine.

Example

Say I want to serve the user service in a different process and communicate via IPC channel, I just have to do this:

// src/service/user.ts
declare global {
    namespace app {
        namespace service {
            const user: ModuleProxy<User>
        }
    }
}

export default class User {
    constructor(private name?: string) {}

    // Any method that will potentially be called remotely should be async.
    async getName() {
        return this.name;
    }

    // Static method getInstance() is used to create the singleton instance.
    static getInstance() {
        return new this("Mr. Handsome");
    }
}
// src/remote-service.ts
import { App } from "./app";

(async () => {
    let service = await App.serve("/tmp/my-app/remote-service.sock");

    service.register(app.service.user);

    console.log("Service started!");
})();

Just try ts-node --files src/remote-service (or node dist/remote-service), and the service will be started immediately.

And in index.ts, connect to the service before using remote functions:

// index.ts
import { App } from "./app";

(async () => {
    let service = await App.connect("/tmp/my-app/remote-service.sock");

    service.register(app.service.user);

    // Access the instance in local style but actually remote.
    console.log(await app.service.user.instance().getName()); // Mr. Handsome
})();

Hot-reloading in Remote Service

The local watcher may notice the local file has changed and try to reload the local module (and the local singleton), however, it will not affect any remote instances, that said, the instance served remotely can still be watched and reloaded on the remote server individually.

In the above example, since the remote-service module imports app module as well, which starts the watcher, when the user module is changed, the remote-service will reload the module as expected, and the index calls it remotely will get the new result as expected.

Generator Support

Since version 3.3, Alar supports generators (and async generators) in both local call and remote call contexts.

// src/service/user.ts
declare global {
    namespace app {
        namespace service {
            const user: ModuleProxy<User>
        }
    }
}

export default class User {
    // ...
    async *getFriends() {
        yield "Jane";
        yield "Ben";
        yield "Albert";
        return "We are buddies";
    }
}

// index.ts
(async () => {
    // Whther calling the local instance or a remote instance, the following 
    // program produce the same result.

    let generator = app.service.user.instance().getFriends();

    for await (let name of generator) {
        console.log(name);
        // Jane
        // Ben
        // Albert
    }

    // If want to get the returning value, just call await on the generator.
    // NOTE: this syntax only works with Alar framework, don't use it with 
    // general generators.
    console.log(await generator); // We are buddies

    // The following usage gets the same result.

    let generator2 = app.service.user.instance().getFriends();

    while (true) {
        let { value, done } = await generator2.next();

        console.log(value);
        // NOTE: calling next() will return the returning value of the generator
        // as well, so the output would be:
        //
        // Jane
        // Ben
        // Albert
        // We are buddies

        if (done) {
            break;
        }
    }
})();

Dependency Injection

Since 3.5.0, Alar add a new method inject(route?: any) to allow you setting up dependency for a specific class in a handy way, check this example:

class Article {
    @app.service.user.inject()
    protected user: User;

    getAuthorName(): Promise<string> {
        return this.user.getName();
    }
}

(async () => {
    var article = new Article;
    console.log(await article.getAuthorName());
})();

Be noticed that this method differs from the usage of assigning the instance to an variable, if you use the syntax below to add the instance to a class property, it will break the hot-reloading feature that Alar provided.

class Article {
    protected user = app.services.user.instance(); // DON't do this
}

This syntax will make a strong reference to the user module, which will not allow the program to refer to the new instance after reloading the user module. But, when using the inject() method, which ships with instance() under the hood, the hot-reloading model will still work fine.

However, Alar doesn't provide a way to inject new instances dynamically, since every new instance will create a strong reference itself, that makes that kind of injection less useful.

For more details, please check the API documentation.

Current Tags

  • 4.3.3                                ...           latest (7 days ago)

67 Versions

  • 4.3.3                                ...           7 days ago
  • 4.3.2                                ...           22 days ago
  • 4.3.1                                ...           a month ago
  • 4.3.0                                ...           a month ago
  • 4.2.0                                ...           a month ago
  • 4.1.1                                ...           a month ago
  • 4.1.0                                ...           a month ago
  • 4.0.8                                ...           a month ago
  • 4.0.7                                ...           a month ago
  • 4.0.6                                ...           2 months ago
  • 4.0.5                                ...           2 months ago
  • 4.0.4                                ...           2 months ago
  • 4.0.3                                ...           2 months ago
  • 4.0.2                                ...           2 months ago
  • 4.0.1                                ...           2 months ago
  • 4.0.0                                ...           2 months ago
  • 3.6.1                                ...           2 months ago
  • 3.6.0                                ...           3 months ago
  • 3.5.1                                ...           3 months ago
  • 3.5.0                                ...           4 months ago
  • 3.4.5                                ...           5 months ago
  • 3.4.4                                ...           6 months ago
  • 3.4.3                                ...           6 months ago
  • 3.4.2                                ...           6 months ago
  • 3.4.1                                ...           6 months ago
  • 3.4.0                                ...           6 months ago
  • 3.3.7                                ...           6 months ago
  • 3.3.6                                ...           6 months ago
  • 3.3.5                                ...           6 months ago
  • 3.3.4                                ...           7 months ago
  • 3.3.3                                ...           7 months ago
  • 3.3.2                                ...           7 months ago
  • 3.3.1                                ...           7 months ago
  • 3.3.0                                ...           7 months ago
  • 3.2.1                                ...           7 months ago
  • 3.2.0                                ...           7 months ago
  • 3.1.2                                ...           7 months ago
  • 3.1.1                                ...           8 months ago
  • 3.1.0                                ...           8 months ago
  • 3.0.3                                ...           8 months ago
  • 3.0.2                                ...           8 months ago
  • 3.0.1                                ...           8 months ago
  • 3.0.0                                ...           8 months ago
  • 2.2.6                                ...           8 months ago
  • 2.2.5                                ...           8 months ago
  • 2.2.4                                ...           8 months ago
  • 2.2.3                                ...           8 months ago
  • 2.2.2                                ...           8 months ago
  • 2.2.1                                ...           8 months ago
  • 2.2.0                                ...           8 months ago
  • 2.1.1                                ...           8 months ago
  • 2.1.0                                ...           8 months ago
  • 2.0.3                                ...           8 months ago
  • 2.0.2                                ...           8 months ago
  • 2.0.1                                ...           8 months ago
  • 2.0.0                                ...           8 months ago
  • 1.2.0                                ...           8 months ago
  • 1.1.2                                ...           8 months ago
  • 1.1.1                                ...           8 months ago
  • 1.1.0                                ...           8 months ago
  • 1.0.6                                ...           8 months ago
  • 1.0.5                                ...           8 months ago
  • 1.0.4                                ...           8 months ago
  • 1.0.3                                ...           8 months ago
  • 1.0.2                                ...           8 months ago
  • 1.0.1                                ...           8 months ago
  • 1.0.0                                ...           8 months ago
Maintainers (1)
Downloads
Today 0
This Week 0
This Month 94
Last Day 0
Last Week 28
Last Month 207
Dependencies (17)
Dev Dependencies (8)

Copyright 2014 - 2016 © taobao.org |