1.1.0 • Published 6 months ago

@startier/ohrid v1.1.0

Weekly downloads
-
License
MIT
Repository
-
Last release
6 months ago

@startier/ohrid

A library for managing distributed nodes.

How does it work?

  • You may define Services, Methods and Entrypoints.

  • Services may contain Methods and Entrypoints.

  • Services can be started, creating a Node.

  • Methods can be invoked by Nodes.

  • Nodes automatically execute Entrypoints if any are present.

  • When a Node invokes a Method:

    • If the Node and the Method are in the same Service, the Method gets run on the same Node that invoked it.
    • If the Node and the Method are in a different Service, a Remote call is made.
  • Remote calls are handled by communication Drivers.

!NOTE Features like network connections, server / client handling, load balancing, etc... are not defined by the library and their handling is entierly up to the driver, if you need a fully-featured driver you can use @startier/ohrid-jsonrpc-driver.

Usage

  • Install the package using npm (or anything compatible):
npm install @startier/ohrid
  • Read the output of the help command:
npx ohrid help
  • Install a communication driver (for example: @startier/ohrid-local-driver) and initialize the project:
npm install @startier/ohrid-local-driver
npx ohrid init --driver=@startier/ohrid-local-driver
  • Optional Edit the base path in services.json:

!NOTE
This is required when you use TypeScript and the path should be set to where tsc outputs the .js files.

{
  "path": "dist"
}
  • Optional Create a service (or a couple of them):

!NOTE
This is recommended when you are using a non-trivial driver that needs configuration.

npx ohrid new service my-service --entrypoint=my-service-entrypoint
  • Create a method (or a couple of them):
npx ohrid new method my-method --service=my-service
  • Modify your methods and services (see the API reference section for more info)

  • Run a service (you can run a service without creating a config for it, it will inherit the default config)

!NOTE
A service without an entrypoint will run forever (or until the driver decides to exit).

npx ohrid start my-service

File Structure

  • ./services.json: the configuration file
  • ./package.json: npm/node package file
  • ./rpc/: RPC method directory
    • ./*/index.[ts|js]: RPC method
  • ./src/: entrypoint directory
    • ./**/*/*.[ts|js]: entrypoint
  • ./drivers/: communication driver directory
    • ./*/index.[ts|js]: communication driver

Configuration

!WARNING Any flag that isn't explicitly defined by a command overrides this configuration (ex: --services.test.settings.doFlagsWork= would override the doFlagsWork setting on the service test.)

The configuration follows the following schema:

{
    path?: string;
    typescript?: boolean;
    driver?: ServiceConfig["driver"];
    settings?: ServiceConfig["settings"];
    docker?: DockerConfig;
    exports?: "named" | "default";
    services?: Record<string, ServiceConfig>;
    import?: string[];
    export?: string[];
}

ServiceConfig = {
  entrypoint?: string;
  amount?: number;
  settings?: Record<string, object | string | number | boolean>;
  driver?: string;
}

DockerConfig = {
  from?: string;
  script?: string;
  image?: string;
}
  • path: location of compiled JavaScript files, defaults to "."
  • typescript: should the new command generate .ts files, defaults to true if "typescript" is a dependency or devDependency in package.json
  • driver: the default communication driver that will be used if no driver is specified for a service
  • settings: the default service settings that get merged with the service settings (defined by external libraries, drivers, etc..)
  • docker: configuration used by npx ohrid generate docker
    • docker.from: the source image used for the generated Dockerfile
    • docker.script: the npm run script used in the build step in the Dockerfile
    • docker.image: the image used by docker-compose.yml, will generate a script called ./build-image.sh that runs docker with the correct arguments if set, otherwise the correct arguments will be passed to the docker-compose.yml directly
  • exports: sets the export style for code generated by the new command
  • services: service configuration
    • services[name].entrypoint: the file that gets run when the service starts, otherwise the service will run forever and only execute methods when requested
    • services[name].amount: how many copies of the service should get added to the docker-compose.yml file
    • services[name].settings: service settings for external libraries or drivers
    • services[name].driver: the communication driver used by the service
  • import: sub-directories and packages where there are other services.json files to import services and methods from
  • export: the methods that should be exported for importing using import in other services.json files

API Reference

@startier/ohrid

Log

A function type that is used to print information to the console.

Signature:

function Log(
  type: "info" | "output" | "error" | "debug" | "warning",
  message: string
): void;

Context

An object that is used for communication between nodes.

This is used by communication drivers to provide a transport layer to ohrid.

Properties:

  • log: Log: the default logger passed down by the creator of the driver
  • exit: boolean: an observed value that will stop the node when set to true
  • remoteCall: function: a function that gets called when the node invokes a method from an other service
    • Signature:
function remoteCall<TService extends string, TParams extends any[], TReturn>(
  method: Method<TService, TParams, TReturn>,
  ...params: TParams
): Promise<Awaited<TReturn>>;

Driver

A communication driver for handling remote calls.

Properties

  • createNode: function: a function that gets called when a context for a node needs to be created
    • Signature:
function createNode(
  name: string,
  config: ServiceConfig,
  rpcMethods: Record<string, Method>,
  log: Log
): Context;
  • handleDockerCompose: function: a function that gets called when a service uses this driver and needs to generate a docker-compose.yml
    • Signature:
function handleDockerCompose(item: {
  service: ServiceConfig;
  dockerServiceName: string;
  appendLine: (text: string): void;
  block: (cb: (): Promise<void>): Promise<void>;
  store: Record<string, string | undefined>;
}): Promise<void>;
  • getDockerfileExtensions: function: a function that gets called when a service uses this driver and needs to generate a Dockerfile
    • Signature:
function getDockerfileExtensions(
  place:
    | "beforeDeps"
    | "afterDeps"
    | "beforeBuild"
    | "afterBuild"
    | "beforeRunner"
    | "afterRunner",
  serviceConfigs: Record<string, ServiceConfig> | undefined
): string;

Method<TService extends string, TParams extends any[], TReturn>

An invocable method returned by declareMethod().

Properties

  • createCaller: function: a function that gets called when a Client needs to invoke a method locally.
    • Signature:
function createCaller(
  context: Context
): (...params: TParams): Promise<Awaited<TReturn>>;
  • service: TService: the service's name that is allowed to run this method
  • name: string: the method's unique name

Client

An abstraction over Context that is used by methods and entrypoints.

Properties

  • invoke: function: this function should be called when a Method needs to be invoked
    • Signature:
function invoke<TService extends string, TParams extends any[], TReturn>(
  method:
    | Method<TService, TParams, TReturn>
    | Promise<{ default: Method<TService, TParams, TReturn> }>,
  ...params: TParams
): Promise<Awaited<TReturn>>;
  • waitForExit: function: this function returns a promise that resolves when the node finishes with operation (or due to Context.exit being set to true)
    • Signature:
function waitForExit(): Promise<void>;
  • log: Log: the default logger passed by the driver

client

A function that can wrap a Context into a Client.

Signature:

function client(context: Context): Client;

declareMethod

Wrap a function and service name into a Method to be used as a default export of a RPC method.

Makes the function be remote-callable by nodes that aren't the correct service when invoked using Client.invoke().

Signature:

function declareMethod<TService extends string, TParams extends any[], TReturn>(
  service: TService,
  method: (context: Client, ...params: TParams): TReturn
): Method<TService, TParams, TReturn>;

@startier/ohrid/service

runService

Runs a service Context with an optional entrypoint.

This command is equivalent to how the command npx ohrid start <service>, but the driver and the context must be created manually.

Signature:

function runService(
  context: Context,
  entrypoint?: (client: Client): Promise<boolean | void>
): Promise<void>