Skip to content

Helper libraries

A helper library is a namespace of functions available to formulas as helpers.<namespace>.<fn>(). The core library is global and also flattened onto helpers.*. Organisation libraries are yours, under any name but core and the names of core’s own functions (trim, date, num…): a library called trim would take the place of helpers.trim in every formula that calls it.

Each library ships a manifest. The manifest is what the formula editor’s autocomplete, the checks made when a library is published and the AI assistant all read, so keep descriptions and examples honest.

{
"namespace": "acme",
"version": "1.0.0",
"scope": "org",
"functions": [
{
"name": "accountCode",
"description": "The ACME account code for a customer name.",
"params": [{ "name": "name", "type": "string" }],
"returns": "string",
"examples": [{ "call": "helpers.acme.accountCode(\"Acme Pty\")", "result": "AC-ACMEPTY" }]
}
]
}

Settings → Formulas & helpers lists this organisation’s libraries — each with its version, how many functions it declares and when it last changed — and the ones Sloose supplies, core among them. Every row opens the library’s own page. An administrator makes, edits and publishes a library there; every member can read one.

  • + New library asks for a name — what a formula writes after helpers.: lower-case letters, digits and _, starting with a letter — and opens the new library’s page with a first function to start from. Nothing is saved until it is published; until then the page keeps what you have written in that browser tab, through a reload.
  • A library’s page has its code and its manifest, each in a code editor (in the code editor Tab indents; press Ctrl+M first — Ctrl+Shift+M on a Mac — to move on with Tab; if it is slow to load, the page says so and offers Reload, keeping what you have written), the functions the manifest declares, and Try it: pick a function, give it arguments, and run the code on the page — published or not — in the sandbox formulas run in. An argument is read as JSON when it is JSON (12, true, "text") and as the text typed otherwise. Ask AI writes a function into it from a description (below).
  • Publish — it names the version it will make, Publish as v4 — publishes what is on the page as the next version, checked as any publish is. An edit is a draft until then: Discard puts back what is published, and leaving the page asks first. If somebody else publishes while you edit, the page says so before you publish over them. When anything uses the library, Publish says what it would do first:
    • templates use the new version from their next import;
    • imports not finished use it from now, and so do a connection’s own defaults — a module’s default match rule, a field’s default mapping — in every import that leaves them in place;
    • each run in progress keeps the version it started with until it ends;
    • an import that keeps an older version (below) goes on as it is. It also lists every formula that calls a function the new version takes away. That is read from the formulas themselves, so a name inside a string is not a call, and a helpers a formula binds for itself is not Sloose’s. A formula that reaches helpers by a name it works out (helpers[name], a direct eval), or a template that cannot be read, is listed as one that may call it. A template private to somebody else is counted there and never named.
  • Versions lists every publish — what it added or removed, who published it and when, and who uses it — shows any version’s code, and makes an older one current again.
  • Used by, on the list and beside a library’s version, counts the templates, the imports not finished and the connections’ defaults that call it, the ones that may, and the runs in progress holding it.
  • Remove library, at the foot of the page, takes it and every version of it away, and says first what that does to the formulas that call it. What it showed is checked again as the library goes: if an import began calling it, or kept a version of it, while you were deciding, nothing is removed and you are asked again with what removing it does now.

Sloose’s own core has a page too, read only: its functions, reached as helpers.core.trim(…) and, for short, helpers.trim(…).

Ask AI, at the top of a library’s page beside Publish, writes a function into the library from a description (“turn an account number like AC-00412 into 412”), or changes one already there (“make clean drop a trailing full stop”). You can give it values to try the function on.

  • It works on the page’s draft. The library’s code and manifest, as they stand on the page and published or not, go with the question. The function comes back in place: a new one after the last, or the one of the same name rewritten. Every other line of the code is as you had it.
  • The library with it in is checked on the server before you see it. Sloose compiles it as a publish would, loads it in the sandbox your libraries run in, runs the examples the AI wrote, and shows you each result.
  • What comes back is a proposal, shown as a change: the lines it adds and removes in the code and in the manifest. It is text until you take it. Nothing in the page runs it, Try it stays shut while it waits, and your draft is as it was.
    • Use this puts it in your draft. Try it then runs it, and Publish makes it a version.
    • No thanks leaves your draft as it was.
    • Not quite… says what is wrong and asks again from the proposal. Your draft stays as it was through every round, so No thanks after any of them puts back what you had.
  • It is not offered on a library your configuration repository publishes. That page is read only; change the library in the repository, or take it over first.
  • It is priced like a formula, by how much it reads: 3–12 credits, the question and the library’s code included. It reserves 12 before it is asked and settles on what it read, so a balance under 12 is refused (see AI credits).

The values you give it to try follow your organisation’s AI data setting like any other value: the model sees them as they are under Allowed, as shapes under Masked, and not at all under Never. Under the last two the examples it is checked against are its own, and the answer says the model did not see your values as they are.

A library’s source is a JavaScript module in one shape — the one the SDK writes in a configuration repository’s libraries/<namespace>/src/index.ts:

import { defineLibrary } from '@sloose/sdk/library';
import manifest from '../manifest.json';
export default defineLibrary({
manifest,
functions: {
accountCode(name) {
const slug = String(name ?? '').toUpperCase().replace(/[^A-Z0-9]+/g, '');
return slug ? `AC-${slug}` : '';
},
},
});

Those two imports are the only ones a library may have, and its default export is the only export. It may import nothing else — no other modules, no import(), no import.meta — and may not await as it loads. TypeScript is fine in a repository; sloose push strips the types before it sends.

Where it runs decides what it can reach, and there are two places:

  • On the server — checking a library when it is published, trying a function, checking a formula for the AI — it runs in a sandbox with no network, no storage and nothing of the API’s.
  • In the browser, while somebody maps fields or runs an import, it runs as a formula does: in that person’s own session, able to reach whatever the page can. That is the spreadsheet’s trust model — the organisation’s own code, written by its administrators, run by its members — and it is why publishing a library is an administrator’s act. Treat a library’s code as you would a macro in a shared workbook.

When a library is published — pushed, or sent to PUT /orgs/{org}/libraries/{namespace} — the source is compiled once, loaded in the sandbox formulas run in, and checked against its manifest. One that does not load is refused, with the line and column where there is one, and nothing is stored: a push carrying it is refused whole. What every import then runs, in the browser and on the server alike, is what that publish compiled.

How much an organisation’s libraries hold

Section titled “How much an organisation’s libraries hold”

Every import page carries every library the organisation has, and every publish is a version kept for good, so there are four bounds on them. They are the same on every plan and far above what a real configuration uses.

Bound The most Refused as
Libraries an organisation holds 20 LIBRARY_LIMIT
Source across its libraries as they currently are 1 MB LIBRARIES_TOO_LARGE
Versions published in 24 hours, across its libraries, counted from the first of them 50 LIBRARY_PUBLISH_RATE
Source across every version it keeps 25 MB LIBRARY_HISTORY_FULL

A publish past one is refused (409, with the bound’s code, the library’s name, where the organisation stands in holding and what the publish would have brought it to in proposed), from a library’s page, from a restore and from a push alike. Reading and running are never refused, so an organisation at a bound keeps every import working.

  • A page’s publish or a restore that is refused writes nothing.
  • A push is checked before it writes anything, and refused there it has written nothing. Where the bound is met only while the push is being applied (somebody published from a library’s page in between), its libraries are refused together and the answer’s written says what had landed by then: the registry and the annotations it names, which stay. Push again once there is room.
  • A push is judged on what it ends at: one library made smaller and another larger, in one push, is fine where the total is.
  • Only a new library is refused at 20. One the organisation holds takes its next version.
  • A publish that changes nothing makes no version, so a deploy that pushes the same libraries every time meets none of them.
  • Removing a library removes its versions, which is how history is freed. An import that keeps one of those versions then keeps nothing and runs whatever is current, so remove a library no import keeps. Removing one does not give the day’s publishes back.
  • sloose validate warns of the first two for the bundle itself: more than 20 libraries, or more than 1 MB of source together. A warning, since the server decides: a library taken over in the browser is in the bundle and is not published by it.

Formulas & helpers shows how much is used under the list’s heading (“3 of 20 libraries, 312 KB of 1 MB”), and GET /orgs/{org}/libraries answers it as holding.

Published from your repository, or from its page

Section titled “Published from your repository, or from its page”

A library your configuration repository pushes is the repository’s. Its page says so, “published from your configuration repository · read only here”, and nothing on it changes the library: Publish, Make current again and Remove are all closed (409 LIBRARY_MANAGED from the API), because the next push would put the change back, or make a removed library again as a new one. Change it in the repository and push.

An API token publishes as the repository, on every route that changes a library, as its push does: a script holding one may publish, restore or remove a library the repository publishes, and a library it makes is the repository’s. It is refused one taken over in the browser, with the same 409 LIBRARY_MANAGED, which says who publishes the library in managedBy.

To change it on its page instead, Take over in the browser. Nothing is published by taking it over, and the version current now stays current. From then on it is published on its page, and a push leaves it as it is, saying so in its warnings: libraries/<name>: taken over in the browser, so this push left it as it is — without compiling the repository’s copy, so one that no longer loads does not refuse the rest of the push. Let the repository manage it, on the same page, hands it back: the page is read only again, and the next push publishes whatever the repository holds. Publish or discard what the page holds first: the button waits until you have, since a read only page cannot publish it. A library made on its page is managed there from the start, so a push leaves it alone until it is handed over.

Each move is an administrator’s, made in a browser (PUT /orgs/{org}/libraries/{name}/management, which an API token cannot call), and each is written on the page’s recent changes: who moved it, and which way. A move names the library its page read, so one removed and published again under the same name since is not moved (409 LIBRARY_REPLACED): read the page again. A Sloose staff member viewing your organisation may take a library over, but never hand one to the repository, since every API token could then change it after their view had ended.

Every publish is a version — v1, v2, v3 — kept beside the current one (GET /orgs/{org}/libraries/{namespace}/versions). Sending what is already current publishes nothing and changes nothing, so a repository that pushes every library on every deploy makes a version only when a library actually changed. An older version is made current again by publishing it as the next one (…/versions/{n}/restore), so the history stays a straight line.

Every template uses a library as it currently is, and so does every import unless it keeps an older version — which it does only by choice, below. So a publish changes what every other formula calling the library computes from then on: treat it as a change to every template that uses it. A run in progress is the exception: the page running it keeps the libraries it started with until the run ends, and says at authorisation which version of each that is (libraryVersions), so a publish meanwhile is said to leave that run alone.

POST /orgs/{org}/libraries/{namespace}/impact, sent the manifest about to be published, answers what the page’s Publish shows: the functions it takes away, who uses the library, and which templates, imports and connections call a function taken away or may — administrators only, as publishing is. Its answer carries a token. Send it back as impactToken with the publish, and the publish checks again as it is made: if something saved since would now break, it is refused 409 IMPACT_CHANGED with the new answer, rather than published over. A repository’s push sends none and is not checked. POST …/impact sent { "remove": true } answers the same for a removal, and its token goes back as ?impactToken= on DELETE /orgs/{org}/libraries/{namespace}, which is checked and refused the same way.

When the current version cannot answer a call, Validate says so before the run. A formula that calls helpers.telco.isDiallable when the version in force has no such function is refused by name and date: “helpers.telco v4, published 30 Sept 2026, has no isDiallable, which Contacts › Mobile calls”. It offers Keep v3 for this import: the newest earlier version that has every function the import calls of it. Keeping it is a change to that import alone. The import’s bar then says “helpers.telco v3 · kept for this import”, with Use v4 to go back. On the import’s own page, its formulas, row filters, Validate and runs all use v3, and a template saved from it keeps nothing. Rows a row filter leaves out are worked out again under the version in force whenever that changes. A version kept of a library that has since been removed keeps nothing, and Validate says so, with Use the current version. A kept version that cannot be loaded just now (the network dropped, say) stops the run instead, with Try again beside Use the current version: nothing runs on the current version unless you choose it.

Checks made on the server use it too. Ask AI’s formula checks and previews, Explain and the other AI answers about an import run the version it keeps: every such request names the kept versions (keep, libraryId:version), and the server reads each from the organisation’s own stored publish. A request naming a version the organisation does not hold is refused (409 KEPT_VERSION_NOT_FOUND) rather than answered with the current one. The MCP tools test_expression and validate_template work on templates, which keep no version, so they run the current one.

An import may keep an older version of a library instead of the current one (libraryPins on PATCH /orgs/{org}/imports/{id}: namespace → { version, library }). The library is the library’s own id (libraryId in the resolved configuration), so a pin outlives no removal of the library. A publish neither counts such an import as using the new version nor says it breaks it; the versions page says it keeps that version. GET /orgs/{org}/libraries/{namespace}/versions/{n} answers that version’s compiled program, which is what a page runs for an import that keeps it, and the libraryId it belongs to. A page runs it only when that is the pin’s library, since the name may have been removed and published again since the page read its configuration. GET /orgs/{org}/libraries?usage=true answers who uses each library, as the versions route does for one.

To try a function before publishing it, POST /orgs/{org}/libraries/try compiles the library as sent and calls one function with the arguments you give, in the same sandbox; it writes nothing and spends no credits.

Publishing a library — and making an older version current again — is the helper libraries plan feature. Reading a library and running one are not: an organisation that moves to a plan without the feature keeps every library it has, and every import that calls one keeps working. Removing a library needs no feature either.

Report a problem with this page

sloose.com