Skip to main content

Event System

Gofer Engine's Event System's library package has been named handelse.

info

Händelse is the Swedish word for event.

This library is designed to be used as a standalone event system library package if so desired.

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

// ...

The default export of the library is an initialized class of the EventSystemManager. Since this is the default import, you can shorten the name to your liking:

eventsExample.ts
import handelse from '@gofer-engine/handelse';

// ...

If instead you wish to initialize or extend the EventSystemManager class yourself, you can import just the class and initialize your own manager.

eventsExample.ts
import { EventSystemManager } from '@gofer-engine/handelse';

const manager = new EventSystemManager(["custom-reserved-handler"]);

// ...

Global vs. Local Handlers

The handelse library provides both named global event handlers, and unnamed local event handlers. Named global event handlers can be later retrieved outside of the current scope while local event handlers only live within their current scope.

Create a Local Handler

To create a local event handler, use the createLocal method:

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

// ...

const localHandler = eventSystemManager.createLocal();

// ...
Aliases

Alternatively you can use these aliases for the createLocal method: local, createInstance, instance.

Create a Global Handler

To create a global event handler, use the createGlobal method:

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

eventSystemManager.createGlobal('example-global-event-handler');

// ...
Aliases

Alternatively you can use these aliases for the createGlobal method: create, global.

Get a Global Handler

Global Event Handlers can be later retrieved by their referenced handle.

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

// ...

const exampleGlobalEventHandler = eventSystemManager.get(
'example-global-event-handler'
);

// ...
tip

For many use cases, it will be uneccessary to retrieve the handler to perform routine tasks such as publishing or subscribing to an event handler as these actions can be performed from the eventSystemManager class by passing the handler name.

note

As local event handlers are not referencable by name within the manager after creation, the get, sub, pub, unsub and delete methods only support global event handlers. See The Event System Manager.

Event Handling

After creating either a local or global event handler, you can use the handler the following ways:

Subscribing

The sub methos allows adding a subscriber to an event handler. A subscriber is simply a promisable function that returns a boolean. If the subscriber successfully handled the event, then the return should be true. If the subscriber rejected the event, then the return should be false.

eventsExample.ts
// ...

const localSubId = localHandler.sub((event) => {
console.log(event);
return true;
});

// ...

The return of the sub method returns the subscriber id. This id can be later used to unsubscribe this particular subscription.

Aliases

Alternatively, you could use any one of these aliases for the sub method: subscribe, on, do, start, or listen.

Publishing

The pub method provides the ability to publish an event to all subscribers.

eventsExample.ts
// ...

localHandler.pub('Hello World!');

// ...

The pub method returns a promise object, mapping each subscriber's id to it's acceptance of the event.

Aliases

Alternatively, you could use any one of these aliases for the pub method: publish, go, emit, broadcast, or signal

Unsubscribing

The unsub method allows for active subscribers to be removed from the current handler. The input requires the subscriber identifier which is returned when adding the subscriber. See Subscribing

eventsExample.ts
// ...

localHandler.unsub(localSubId);

// ...
Aliases

Alternatively, you could use any of of these aliases for the unsub method: unsubscribe, off, remove, stop, or deafen

You can bulk unsubscribe all subscribers using the removeAll method.

eventsExample.ts
// ...

exampleGlobalEventHandler.removeAll();

// ...

The Event System Manager

Aside from creating event handlers, the Event System Manager provided methods for directly interacting with global event handlers.

Global Subscribing

The sub method works identical to the sub method of the event handler class, with the addition of the handler name as the first argument.

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

// ...

const subId_1 = eventSystemManager.sub('example-global-event-handler', (event) => {
console.log(event);
return true;
});

// ...
Aliases

Alternatively, you could use any one of these aliases for the sub method: subscribe, on, do, start, or listen.

Global Publishing

The pub method works identical to the pub method of the event handler class, with the addition of the handler name as the first argument.

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

// ...

eventSystemManager.pub('example-global-event-handler', 'Hello World!');

// ...
Aliases

Alternatively, you could use any one of these aliases for the pub method: publish, go, emit, broadcast, or signal

Global Unsubscribing

The unsub method works identical to the unsub method of the event handler class, with the addition of the handler name as the first argument.

eventsExample.ts
import eventSystemManager from '@gofer-engine/handelse';

// ...

eventSystemManager.unsub('example-global-event-handler', subId_1);

// ...
Aliases

Alternatively, you could use any of of these aliases for the unsub method: unsubscribe, off, remove, stop, or deafen

You can bulk unsubscribe all subscribers using the removeAllSubs method. This method works similar to the removeAll method of the handler class

eventsExample.ts
// ...

eventSystemManager.removeAllSubs('example-global-event-handler');

// ...

Deleting Handlers

The delete method provides the capability for removing Global Event Handlers.

note

Local Event Handlers do not have a delete method as they naturally live within their scope and are natively garbage collected by the environment. Global Event Handlers live forever within the scope of the running process. So when they are no longer being used, it would be wise to delete the global event handlers to allow native garbage collection processes.

eventsExample.ts
// ...

eventSystemManager.delete('example-global-event-handler');

// ...

The manager also provides the clear method for removing all global event handlers.

eventsExample.ts
// ...

eventSystemManager.clear();
warning

You cannot delete the reserved _ALL_ global event handler. When clearning all global event handlers using the clear() method, the reserved _ALL_ event handler will be reattached.

Typing Event Handlers

If you are using TypeScript in your project, which we highly recommend, then you can pass in generic types to validate event types upon compiling. These generics are available to use when creating local or global event handlers, and can be used to strongly type global handlers retrived using the get method.

Runtime Typing

TypeScript typing can only be used while compiling and not during runtime. For this purpose, we have included the eventType option parameter to help do basic type checking during runtime using the typeof syntax. This will throw errors during runtime if the typeof event does not match the options.eventType if it is not undefined.

You can do additional type checking in your subscriber functions and rejecting malformed data. This eventType option runs the check before hitting subscribers.

eventsExample.ts
// ...

eventSystemManager.global<'foo'|'bar'>('typed-handler', { eventType: 'string' });

const typedHandler = eventSystemManager.get<'foo'|'bar'>('typed-handler');

typedHandler.sub((event) => {
// @ts-expect-error - event is typed as 'foo'|'bar' and not `string`
if (event === 'baz') {
console.log(event);
}
return true;
});

// @ts-expect-error - event is typed as 'foo'|'bar' and not `string`
typedHandler.pub('baz');

// ...
danger

If you manually coerce typescript types in your publisher, then your subscribers may receive events of an unexpected type. It is always recommended to add additional sanitization and data validation processes before sending data to systems susceptable to injection vulnerabilities.

Advanced Options

With the available options object, you can do the following advanced workflows:

  • Subscribe to Global Event Handlers not already existing with the createIfNotExists option set to true
  • Publish to Global Event Handlers not already existing with the createIfNotExists option set to true
  • Prevent errors of creating Global Event Handlers already created with the getIfAlreadyCreated option set to true
eventsExample.ts
// ...

eventSystemManager.sub(
'new-handler',
e => Boolean(console.log(e)),
{ createIfNotExists: true },
);

eventSystemManager.global('new-handler', { getIfAlreadyCreated: true });

// ...
Unique Handlers

Remember that global handlers should be globally unique within the running process. It might be beneficial to use a custom prefix for handlers that you manage if your are using handelse as a library within your application. In Gofer Engine, we prefix most global handlers with gofer: and within channel contexts include the channel id as well.

The _ALL_ Event Handler

Handelse creates a global handler named _ALL_ that cannot be removed or published to directly. This handler is automatically subscribed to all other event handlers. This allows you to listen globally to all events. The Event System Manager provides the method subAll and aliases all, subscribeAll, onAll, doAll, startAll, or listenAll to add subscriber functions to this _ALL_ global event handler.

This reserved handler can be used like any other global event handler with the exception of being able to directly publish to this handler.

Gofer Engine LOGGER Handle

Gofer Engine adds an additional global handler named LOGGER. Subscribing to this event handler, you can setup a custom log receiver in additional to or instead of the default console.log handler.

See Logging for more information.