Referencelink

Membrane Modulelink

This module contains functionality specific to each program.

import { state, nodes, root } from "membrane";
//                                     ↑
//                                     |
//                                     |
//                              The Membrane Module

statelink

The state object is used to keep long-lived data around.

This object is needed because every time a program is updated with new code, a new JavaScript module is created to replace the old one, but module-level variables from the old one are not automatically transferred over. The state object is thus used to share state between the two.

A good rule of thumb is to keep persistent data in the state object.

nodeslink

nodes is the object that holds the dependencies of a program. It holds handles to graph nodes. Using these nodes is how the program communicates with others, makes http request, etc.

Along with the dependencies that you specify, the nodes object comes with two dependencies that are injected by default:

  • nodes.process: used to read metadata about the program. For now just endpointUrl is available.
  • nodes.clock: used to read the time, set timers and cronjobs.

These two, instead of pointing to other programs on your graph, point to special system programs that can provide the corresponding functionality.

The fetch function in Membrane uses nodes.http behind the scenes to actually make requests, and sleep uses nodes.clock

rootlink

root is a handle that references the program's own root node. It can be used to reference any node in the program's graph, for example to emit event, or to specify event handlers.

...

Field methodslink

Fields are the readable nodes in the graph.

$getlink

Reads the value of a scalar field. For non-scalar fields (aka objects), use $query instead.

This method has been deprecated since you can now await a scalar field to get its value:

const value = await nodes.path.to.string;

However, it's still available, so the above could be written like so:

const value = await nodes.path.to.string.$get();

$querylink

Reads subvalues from a values by running a GraphQL query on the node. For example:

// Query two fields on a pull-request
const { title, body } = await nodes.pullRequest.$query("{ title body }");

// Outermost curly braces can be omitted.
const { title, body } = await nodes.pullRequest.$query("title body");

// Nested fields can be queried too!
const { title, comments } = await nodes.pullRequest.$query(
  "title comments { page { body } }"
);
...

Action methodslink

Actions are the callable nodes in the graph. You can think of actions as functions that can be referenced by other programs.

Actions in the UI are rendered in magenta.

$invokelink

Invokes the action returning a promise to its result.

This method has been deprecated since you can now await an action to invoke it:

const result = await nodes.path.to.action({ ... });

However, it's still available. The above could be written with $invoke like so:

const result = await nodes.path.to.action({ ... }).$invoke();

$invokeInlink

Invokes the action after the specified number of seconds.

// Invoke after a minute
nodes.path.to.action.$invokeIn(60);

$invokeAtlink

Invokes the action at the specified date.

// Invoke at the New Year
nodes.path.to.action.$invokeAt(new Date("2023-01-01"));

$cronlink

Invokes the action repeatedly based on the provided cron expression. Unlike traditional cron, however, our cron expressions include seconds. For example:

// Invoke every 15 min
nodes.path.to.action.$cron("0 */15 * * * *");

// A more elaborate example:
//               sec  min   hour   day of month   month   day of week   year
let expression = "0   30   9,12,15     1,15       May-Aug  Mon,Wed,Fri  2018/2";
nodes.path.to.action.$cron(expression);
...

Event methodslink

Events are nodes you can subscribe to in order to receive notifications when they happen.

The program that exposes the event can then $emit it to invoke the handling action registered by all subscribers.

Events in the UI are rendered in purple.

$subscribelink

Subscribes to an event. An event is always handled by an action, so you need to declare an action to handle them. We understand that it would be more convenient to allow callbacks, but callbacks are not graph-referenceable (e.g. what happens when you change the code of the callback?) but actions are, so we use them.

const id = await nodes.pullRequest.closed.$subscribe(root.handler);

// Note that the event is received in the second parameter of the action
export function handler(args, { event }) {
  console.log("The pull request has been closed", event);
}

// Later, to unsubscribe
unsubscribe(id);

Another example demonstrating how to pass arguments to the handler action. Handler args can be used to hold some context about the subscription.

const context = "hello";
nodes.some.event.$subscribe(root.handler({ context }));

// Note that the event is received in the second parameter of the action
export function handler(args, { event }) {
  console.log("Event fired", args.context, event);
}

$emitlink

Emits the event to all subscribers. For example:

root.path.to.event.$emit();

The type of value passed to $emit must match the type declared in the schema. So a String-typed event could be emitted like this:

root.path.to.event.$emit("Something happened");
...

Globalslink

sleeplink

Returns a promise that sleeps for the provided number of seconds.

console.log("Before");
await sleep(60);
console.log("After 1 minute");

unsubscribelink

Unsubscribes to a previously created subscription. For example, to subscribe for only one event:

state.subscription = await nodes.some.event.$subscribe(root.handler());

export handler() {
    console.log('Event fired. Unsubscribing');
    unsubscribe(state.subscription);
}

...