Skip to content

Helper manifest

A helper library is two things: source, which the host evaluates, and a manifest, which describes what is in it.

This page is the manifest’s reference. For what libraries are and how to write one, see Helper libraries.

Because three things need to know what a library offers without running it:

  • The formula editor, for autocomplete and signatures.
  • The checks, which compare a library with its manifest when it is published: a function declared and not implemented, or implemented and not declared, refuses the publish. And Validate reads every formula an import runs: one that calls a function the version in force does not have is an error there, named with the function and the version’s date, with Keep vN for this import beside it. What it cannot name is a call worked out as the formula runs (helpers[name]), which still fails on the row.
  • The AI, which is told what helpers are available so it proposes expressions that will work.

Deriving that by parsing the source would be guesswork. Declaring it is a contract.

The practical consequence: the source and the manifest have to name the same functions. A function the source has and the manifest does not refuses the publish, as one the manifest declares and the source lacks does. Adding a function means adding it in both places.

{
"namespace": "acme",
"version": "1.2.0",
"scope": "org",
"description": "ACME-specific transforms.",
"functions": [
{
"name": "accountCode",
"description": "The ACME account code for a customer name.",
"params": [{ "name": "name", "type": "string" }],
"returns": "string",
"examples": [
{ "call": "helpers.acme.accountCode(row.Customer)", "result": "AC-0042" }
],
"tags": ["accounts"]
}
]
}
Field Required Notes
namespace ✓ Lower-case identifier. Becomes helpers.<namespace>.*.
version ✓ Semver, 1.2.0. Exactly three numeric parts.
scope ✓ org or global. Organisation routes only write org.
description What the library is for.
functions ✓ The list below.
Field Required Notes
name ✓ An identifier.
description ✓ One line. Read by people and by the model.
params ✓ Each with name and type; optional and description allowed.
returns ✓ A type, as a string.
examples { call, result } pairs.
tags For grouping in the editor.

Types are free-form strings, not a checked type system — string, number, Record<string, unknown>. They document intent for a reader and for the model; they are not enforced at run time.

These are not decoration. The description is what somebody sees in autocomplete at the moment they are deciding whether this is the function they want, and it is what the model reads when proposing a formula.

A function described as “formats a code” will be misused. Described as “The ACME account code for a customer name — returns an empty string when the customer is unknown”, it will not.

Examples are the highest-value field after the description, because they show shape and edge cases in less space than prose:

"examples": [
{ "call": "helpers.acme.accountCode('Northwind Traders')", "result": "AC-0042" },
{ "call": "helpers.acme.accountCode('Unknown Ltd')", "result": "''" }
]

Functions are called under their namespace:

helpers.acme.accountCode(row.Customer)

The one exception is the built-in core library, which is also flattened onto helpers.* — so helpers.trim(…) works as well as helpers.core.trim(…). Your own libraries are always namespaced, which keeps them from colliding with each other or with a future built-in.

The manifest’s version is semver, and it is your label for this release of the library. Sloose keeps its own count beside it: every publish is a version — v1, v2, v3 — and none is overwritten, so a library’s page can show what each one held and make an older one current again (which publishes it once more, as the next version).

Every template uses a library as it currently is, and so does every import unless it keeps an older version by choice (libraryPins). So publishing changes what every other formula calling the library computes from then on. Make the semver mean something anyway — a changed return shape is a major bump, whatever it looks like from inside the library — because it is what a person reading the manifest has to go on.

When the library is published, before anything is stored. The manifest is checked against the schema, its namespace must match the path it is written to (NAMESPACE_MISMATCH otherwise), an organisation route will not accept scope: "global" (GLOBAL_LIBRARY) — a global library belongs to the product, not to an organisation — and core, like the name of any built-in helper (trim, date, phone…), is Sloose’s own (RESERVED_NAMESPACE): a library by that name would take the helper’s place. Then the source is compiled and loaded in the sandbox against the manifest, and a library that does not load is refused with what stopped it (LIBRARY_DOES_NOT_LOAD) — see Helper libraries.

The manifest shape is one of Sloose’s versioned contracts, shared by the widget, the server, the SDK and every customer configuration repository. It does not change without a version bump, so a manifest written today keeps working.

By hand, or as part of a configuration repository alongside the source — which is the better arrangement, because the manifest and the code it describes then change in the same commit and get reviewed together.

Report a problem with this page

sloose.com