hfs

For plug-in makers

If the information you are searching for is not in this document, please ask.

A plug-in is a folder with a plugin.js file in it. To install a plugin you just copy the folder into plugins folder. You will find plugins folder near config.yaml, and then in USER_FOLDER/.hfs for Linux and Mac, or near hfs.exe on Windows.

Plug-ins can be hot-swapped, and at some extent can be edited without restarting the server.

Each plug-in has access to the same set of features. Normally you’ll have a plug-in that’s a theme, and another that’s a firewall, but nothing is preventing a single plug-in from doing both tasks.

Exported object

plugin.js is a javascript module, and its main way to communicate with HFS is by exporting things. For example, it can define its description like this

exports.description = "I'm a nice plugin"

The set of things exported goes by the name “exported object”. A plugin can define an init function like this:

exports.init = api => ({
    frontend_css: 'mystyle.css'
})

The init function is called by HFS when the module is loaded and should return an object with more things to add/merge to the exported object. In the example above we are asking a css file to be loaded in the frontend. Since it’s a basic example, you could have simply defined it like this:

exports.frontend_css = 'mystyle.css'

but in more complex cases you’ll need go through the init. Thus, you can decide to return things in the init function, or directly in the exports. If you need to access the api you must use init, since that’s the only place where it is found, otherwise you can go directly with exports. The parameter api of the init is an object containing useful things we’ll see later.

Let’s first look at the things you can export:

Things a plugin can export

All the following properties are optional unless otherwise specified.

WARNING: All the properties above are a bit special and must go in exports only (thus, not returned in init) and the syntax used must be strictly JSON (thus, no single quotes, only double quotes for strings and objects).

FieldDescriptor

Currently, these properties are supported:

Based on type, other properties are supported:

api object

The api object you get as parameter of the init contains the following:

Front-end specific

The following information applies to the default front-end, and may not apply to a custom one.

Once your script is loaded into the frontend (via frontend_js), you will have access to the HFS object in the global scope.

The HFS objects contains many properties:

The following properties are accessible only immediately at top-level; don’t call it later in a callback.

Front-end API events

API at this level is done with frontend-events, that you can handle by calling

HFS.onEvent(eventName, callback)

//type callback = (parameters: object) => any

Parameters of your callback and meaning of returned value varies with the event name. Refer to the specific event for further information. HFS object is the same you access globally. Here just for legacy, consider it deprecated.

Some frontend-events can return Html, which can be expressed in several ways

This is a list of available frontend-events, with respective object parameter and output.

Other files

Together with the main file (plugin.js), you can have other files, both for data and javascript to include with require('./other-file'). Notice that in this case you don’t use api.require but classic require because it’s in your plugin folder.

These files have a special meaning:

Publish your plug-in

Suggested method for publishing is to have a dedicated repository on GitHub, with topic hfs-plugin. To set the topic go on the repo home and click on the gear icon near the “About” box. Be sure to also fill the “description” field, especially with words that people may search for.

The files intended to be installed must go in a folder named dist. You can keep other files outside.

If you have platform-dependent files, you can put those files in dist-PLATFORM or dist-PLATFORM-ARCHITECTURE. For example, if you want some files to be installed only on Windows with Intel CPUs, put them in dist-win32-x64.

Possible values for platform are aix, darwin, freebsd, linux, openbsd, sunos, win32.

Possible values for CPUs are arm, arm64, ia32, mips, mipsel, ppc, ppc64, s390, s390x, x64.

You can refer to these published plugins for reference, like

Published plugins are required to specify the apiRequired property.

It is possible to publish different versions of the plugin to be compatible with different versions of HFS. To do that, just have your other versions in branches with name starting with api. HFS will scan through them in inverted alphabetical order searching for a compatible one.

React developers

Most React developers are used to JSX, which is not (currently) supported here. If you want, you can try solutions to JSX support, like transpiling. Anyway, React is not JSX, and can be easily used without.

Any time in JSX you do

<button onClick={() => console.log('hi')}>Say hi</button>

This is just translated to

h('button', { onClick: () => console.log('hi') }, 'Say hi')

Where h is just import { createElement as h } from 'react'.

Internationalization (i18n)

To make your plugin multi-language you can use HFS.t function in javascript, like this: HFS.t("Hello!"). Now, to add translations, you’ll add files like hfs-lang-XX.json to your plugin (same folder as plugin.js), where XX is the language code. The system is basically the same used to translate the rest of HFS, and you can read details here.

In the previous example “Hello!” is used both as key for translation and as for default text. If you want to separate these 2 things, just pass 2 parameters, key and default text. Eg: HFS.t('greeting', "Hello!").

If you need to pass variables in the text, introduce a third parameter in the middle. Eg: HFS.t('filter_count', {n:filteredVariable}, "{n} filtered")

API version history