puter/packages/backend/doc/contributors/modules.md

3.1 KiB

Puter Kernel Moduels and Services

Modules

A Puter kernel module is simply a collection of services that run when the module is installed. You can find an example of this in the run-selfhosted.js script at the root of the Puter monorepo.

Here is the relevant excerpt in run-selfhosted.js at the time of writing this documentation:

const {
    Kernel,
    CoreModule,
    DatabaseModule,
    LocalDiskStorageModule,
    SelfHostedModule
} = (await import('@heyputer/backend')).default;

console.log('kerne', Kernel);
const k = new Kernel();
k.add_module(new CoreModule());
k.add_module(new DatabaseModule());
k.add_module(new LocalDiskStorageModule());
k.add_module(new SelfHostedModule());
k.boot();

A few modules are added to Puter before booting. If you want to install your own modules into Puter you can edit this file for self-hosted runs or create your own script that boots Puter. This makes it possible to have deployments of Puter with custom functionality.

To function properly, Puter needs CoreModule, a database module, and a storage module.

A module extends AdvancedBase and implements an install method. The install method has one parameter, a Context object containing all the values kernel modules have access to. This includes the services Container.

A module adds services to Puter.eA typical module may look something like this:

class MyPuterModule extends AdvancedBase {
    async install (context) {
        const services = context.get('services');

        const MyService = require('./path/to/MyService.js');
        services.registerService('my-service', MyService, {
            some_options: 'for-my-service',
        });
    }
}

Services

Services extend BaseService and provide additional functionality for Puter. They can add HTTP endpoints and register objects with other services.

When implementing a service it is important to understand Puter's boot sequence

A typical service may look like this:

class MyService extends BaseService {
    static MODULES = {
        // Use node's `require` function to populate this object;
        // this makes these available to `this.require` and offers
        // dependency-injection for unit testing.
        ['some-module']: require('some-module')
    }

    // Do not override the constructor of BaseService - use this instead!
    async _construct () {
        this.my_list = [];
    }

    // This method is called after _construct has been called on all
    // other services.
    async _init () {
        const services = this.services;

        // We can get the instances of other services here
        const svc_otherService = services.get('other-service');
    }

    // The service container can listen on the "service event bus"
    async ['__on_boot.consolidation'] () {}
    async ['__on_boot.activation'] () {}
    async ['__on_start.webserver'] () {}
    async ['__on_install.routes'] () {}
}