Publish a helper library
const url = 'https://api.sloose.com/orgs/org_9f3c/libraries/acme';const options = { method: 'PUT', headers: { 'If-Match': '"7"', Authorization: 'Bearer <token>', 'Content-Type': 'application/json' }, body: '{"manifest":{"namespace":"example","version":"example","scope":"global","description":"example","functions":[{"name":"example","description":"example","params":[{"name":"example","type":"example","optional":true,"description":"example"}],"returns":"example","examples":[{"call":"example","result":"example"}],"tags":["example"]}]},"source":"example","impactToken":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request PUT \ --url https://api.sloose.com/orgs/org_9f3c/libraries/acme \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --header 'If-Match: "7"' \ --data '{ "manifest": { "namespace": "example", "version": "example", "scope": "global", "description": "example", "functions": [ { "name": "example", "description": "example", "params": [ { "name": "example", "type": "example", "optional": true, "description": "example" } ], "returns": "example", "examples": [ { "call": "example", "result": "example" } ], "tags": [ "example" ] } ] }, "source": "example", "impactToken": "example" }'Publishes the library as its next version (v1 for a new one). Before anything is written the manifest is validated against the helper manifest contract — its namespace must match the path and may not be core or the name of one of Sloose’s own helpers (trim, date…), which a library by that name would replace — and the source is compiled and loaded in the sandbox against that manifest, so a library that does not load is refused rather than stored. The library’s module, as the SDK writes it: import { defineLibrary } from '@sloose/sdk/library'; import manifest from '../manifest.json'; export default defineLibrary({ manifest, functions: { … } }); — those two imports and nothing else, one default export, JavaScript (the SDK strips TypeScript before it sends).
Sending what is already current publishes nothing: no new version, no change to the registry version, and published: false — so a push that sends the same library every time costs nothing. Every template, and every import that keeps no older version of it, uses the library as it currently is; an import may keep an older one by choice (libraryPins on the import), and nothing else pins.
Publishing is the helper_libraries plan feature (403 FEATURE_HELPER_LIBRARIES without it); reading and running a library are open on every plan.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The organisation id the session was issued for. A token for one organisation can never read another.
Example
org_9f3cThe organisation id the session was issued for. A token for one organisation can never read another.
The library namespace, as it appears in expressions.
Example
acmeThe library namespace, as it appears in expressions.
Header Parameters
Section titled “Header Parameters”The registryVersion this change was made against, as "7" or 7 — a weak tag (W/"7") is refused, since If-Match uses strong comparison. Sent, the write is refused with 409 (code: REGISTRY_CONFLICT, the current version in the body) when the organisation has moved since, and otherwise answers the version it made: one past the version sent for a write made in one transaction, or the version sent itself for a request that asks for no change (an empty PATCH /config/writing), since it makes none. A bundle (POST /config/bundle) is several transactions and only its first waits on this version: it answers the version its own bumps took the organisation to — each part bumps only when it writes something, so a push of what is already there answers the version sent — and none — nor an etag — when another write landed between them; one landing after them, while its templates are saved, withholds the etag alone. Omitted, there is no condition, and * is no condition either — and a write with no condition answers no registryVersion (nor, from a bundle, an etag), since nothing says which version it was made from. A value that is neither is 400 (code: BAD_IF_MATCH) rather than ignored — a precondition silently dropped looks like conflict detection and is not.
Example
"7"The registryVersion this change was made against, as "7" or 7 — a weak tag (W/"7") is refused, since If-Match uses strong comparison. Sent, the write is refused with 409 (code: REGISTRY_CONFLICT, the current version in the body) when the organisation has moved since, and otherwise answers the version it made: one past the version sent for a write made in one transaction, or the version sent itself for a request that asks for no change (an empty PATCH /config/writing), since it makes none. A bundle (POST /config/bundle) is several transactions and only its first waits on this version: it answers the version its own bumps took the organisation to — each part bumps only when it writes something, so a push of what is already there answers the version sent — and none — nor an etag — when another write landed between them; one landing after them, while its templates are saved, withholds the etag alone. Omitted, there is no condition, and * is no condition either — and a write with no condition answers no registryVersion (nor, from a bundle, an etag), since nothing says which version it was made from. A value that is neither is 400 (code: BAD_IF_MATCH) rather than ignored — a precondition silently dropped looks like conflict detection and is not.
Request Bodyrequired
Section titled “Request Bodyrequired”object
object
object
object
object
The library’s module, as the SDK writes it: import { defineLibrary } from '@sloose/sdk/library'; import manifest from '../manifest.json'; export default defineLibrary({ manifest, functions: { … } }); — those two imports and nothing else, one default export, JavaScript (the SDK strips TypeScript before it sends). At most 256 KB.
The token of the POST …/impact answer the person confirmed. Sent, the publish checks what it would break again as it is made, and is refused 409 IMPACT_CHANGED — with the new answer — when that has changed. A caller that asked nothing (a repository’s push) sends none and is not checked.
Responses
Section titled “Responses”The library as it now stands, and — when the write was sent with If-Match — the registry version it is at: one past the version sent when it published, the version sent itself when it published nothing.
object
object
The library’s own id. A name removed and published again is another library under a new id (#1040), so a page that changes who publishes it names this (PUT …/management), and an import’s kept version names it too (LibraryPin.library).
The manifest’s own semver: the author’s label, not which publish this is.
Which publish is current: 3 for v3 (…/versions). 0 for a library stored before versions were kept.
object
The module as written, when the route answers it (?source=true on the list).
sdk when the organisation’s configuration repository publishes it: its page is read only, and a person publishing, restoring or removing it is refused (409 LIBRARY_MANAGED) until it is taken over (PUT …/management), while an API token, which publishes as the repository, may. Null when people publish it on its page, where a push leaves it alone and says so and an API token is refused. A library a push or a token makes is the repository’s.
Whether this made a new version. False when what was sent is already current.
The version the organisation is at after this write, to send as the next write’s If-Match: one past the version you sent in If-Match for a write made in one transaction (a bundle, several, answers the version its own bumps took the organisation to, or none when another write landed between them), or that version itself for a request that asks for no change (an empty PATCH /config/writing, which writes nothing and makes no version; sending the value the organisation already has still writes, and moves it). Only a write sent with a version in If-Match answers one. Without (no header, or *), the write lands on whatever version the organisation is at, and nothing on the server knows which version your picture of the configuration was built from: another administrator’s change may be inside the number, and sent back as If-Match it would let your next write overwrite that change unseen. So it is left out. To write again, read the configuration first (its GET answers the version), or send If-Match.
Example
{ "library": { "scope": "global", "managedBy": "sdk" }}The request — its body or its query — did not match the schema. issues carries the Zod issue list.
The error envelope every non-2xx answer uses.
object
Human-readable explanation.
Machine-readable reason. Absent on a few legacy 400s.
Examplegenerated
{ "error": "example", "code": "example"}No bearer, or one that no longer resolves: a session that is unknown or expired (code: AUTH_REQUIRED), or an API token that was revoked or expired (TOKEN_REVOKED, TOKEN_EXPIRED), whose organisation is gone (ORG_NOT_FOUND) or whose member or user row is (MEMBERSHIP_GONE).
The error envelope every non-2xx answer uses.
object
Human-readable explanation.
Machine-readable reason. Absent on a few legacy 400s.
Examplegenerated
{ "error": "example", "code": "example"}Not an administrator, or this plan cannot publish helper libraries (code: FEATURE_HELPER_LIBRARIES). Like every route behind a session: a session signed in but in no organisation is refused code: NO_ORG, a banned person code: BANNED, and a closed organisation code: ORG_CLOSED.
The error envelope every non-2xx answer uses.
object
Human-readable explanation.
Machine-readable reason. Absent on a few legacy 400s.
Examplegenerated
{ "error": "example", "code": "example"}Or a bound on what an organisation’s libraries hold is met, the same on every plan (holding says where it stands with the caps, proposed what this would have brought it to): LIBRARY_LIMIT — it would hold more than 20 libraries; LIBRARIES_TOO_LARGE — its libraries would come to more than 1 MB of source; LIBRARY_HISTORY_FULL — the versions it keeps would come to more than 25 MB; LIBRARY_PUBLISH_RATE — more than 50 versions in 24 hours, counted from the first of them. A publish that changes nothing makes no version and meets none of them. Nothing was written. The organisation’s configuration has changed since the version in If-Match (REGISTRY_CONFLICT, the current one in the body); with impactToken, what the publish would break is no longer what the person confirmed (IMPACT_CHANGED, the new answer in impact); or the library is published by the other publisher (LIBRARY_MANAGED, which says who in managedBy): a person is refused one the configuration repository publishes — change it there, or take it over first (PUT …/management) — and an API token, which publishes as the repository, one taken over in the browser.
object
object
object
The organisation’s templates, which each run whichever version is current.
object
How many call one of its functions by name.
How many may reach it without naming the function (helpers[name], or the library read whole), or could not be read: never counted as not using it.
Its imports not finished.
object
How many call one of its functions by name.
How many may reach it without naming the function (helpers[name], or the library read whole), or could not be read: never counted as not using it.
Its connections, by their own defaults: a module’s default match rule and a field’s default mapping, which a run takes from the connection rather than its recipe. A connection that is somebody else’s own is counted, never named.
object
How many call one of its functions by name.
How many may reach it without naming the function (helpers[name], or the library read whole), or could not be read: never counted as not using it.
The organisation’s runs in progress.
object
The version of this library the run started with, which it keeps until it ends — 0 for what came before v1 (the one Sloose ships, or one stored before versions were kept); null when the run did not say which libraries it holds. A run that said, and named no version of this one, holds none of it and is not listed.
True when what the run holds is of a library since removed — its record names another library’s id — so version is no version of this one.
Imports not finished that keep an older version of this library instead of the current one (“Keep v3 for this import”): counted in no use of the current version, and broken by no publish.
object
The version it keeps.
Only on the answer to a removal where Sloose ships a library under the same name: the functions this import calls that the Sloose library also has, which it will use instead of the version it keeps.
The functions the version current now has and the manifest sent does not.
object
object
The functions it calls that the new version does not have, and where.
object
object
In a recipe — row-filter: which rows take part. gate: a module’s which rows. guard: a module’s update-only-when. field: a field’s formula. search: a lookup’s search value. key: the value a module’s match compares. In a connection’s own defaults — default-match: a module’s default match rule. default-field, default-search, default-key: the same three parts of a field’s default mapping.
The flow, for a place in a recipe.
object
In a recipe — row-filter: which rows take part. gate: a module’s which rows. guard: a module’s update-only-when. field: a field’s formula. search: a lookup’s search value. key: the value a module’s match compares. In a connection’s own defaults — default-match: a module’s default match rule. default-field, default-search, default-key: the same three parts of a field’s default mapping.
The flow, for a place in a recipe.
object
The functions it calls that the new version does not have, and where.
object
object
In a recipe — row-filter: which rows take part. gate: a module’s which rows. guard: a module’s update-only-when. field: a field’s formula. search: a lookup’s search value. key: the value a module’s match compares. In a connection’s own defaults — default-match: a module’s default match rule. default-field, default-search, default-key: the same three parts of a field’s default mapping.
The flow, for a place in a recipe.
object
In a recipe — row-filter: which rows take part. gate: a module’s which rows. guard: a module’s update-only-when. field: a field’s formula. search: a lookup’s search value. key: the value a module’s match compares. In a connection’s own defaults — default-match: a module’s default match rule. default-field, default-search, default-key: the same three parts of a field’s default mapping.
The flow, for a place in a recipe.
Connections whose own defaults call a function taken away, or may.
object
The functions it calls that the new version does not have, and where.
object
object
In a recipe — row-filter: which rows take part. gate: a module’s which rows. guard: a module’s update-only-when. field: a field’s formula. search: a lookup’s search value. key: the value a module’s match compares. In a connection’s own defaults — default-match: a module’s default match rule. default-field, default-search, default-key: the same three parts of a field’s default mapping.
The flow, for a place in a recipe.
object
In a recipe — row-filter: which rows take part. gate: a module’s which rows. guard: a module’s update-only-when. field: a field’s formula. search: a lookup’s search value. key: the value a module’s match compares. In a connection’s own defaults — default-match: a module’s default match rule. default-field, default-search, default-key: the same three parts of a field’s default mapping.
The flow, for a place in a recipe.
How many more break that are not the caller’s to see: templates private to somebody else, connections that are somebody else’s own. Counted, never described.
object
What was found to break, as one string. Sent back as impactToken with the publish, it is checked again as the publish is made, and a publish that would now break something this answer did not name is refused (409 IMPACT_CHANGED) — saving a template moves no configuration version, so If-Match alone cannot see one saved while the person was deciding.
object
Who publishes the library: sdk when the configuration repository does (a person was refused), null when it was taken over in the browser (an API token was refused).
object
The library that met the bound. Null where a push of several was refused in its own transaction and which of them met it cannot be told.
The organisation as it stands: what the list shows.
object
Libraries the organisation holds.
UTF-8 bytes of source across its libraries as they currently are.
UTF-8 bytes of source across every version it keeps. Removing a library removes its versions.
Versions published in the last 24 hours, across its libraries.
When the oldest of those leaves the 24 hours, so another may be published where the day’s cap is met. Null with none.
The most of each, the same on every plan.
object
What the publish, or the whole push it is part of, would have brought the organisation to: the figure that is past its bound.
object
Example
{ "code": "REGISTRY_CONFLICT"}The manifest’s namespace does not match the path, it declares global scope, it names core or one of Sloose’s own helpers (helpers.trim, helpers.date…), which a library by that name would replace, or the library does not load — error says what stopped it, with the line and column where there is one.
object
Example
{ "code": "NAMESPACE_MISMATCH"}Libraries cannot be checked just now: the sandbox did not answer (code: SANDBOX_UNAVAILABLE). Or the restore switch is on (code: MAINTENANCE): every route but /health and /robots.txt is refused before anything is read, with Retry-After (the operator’s ADMIN_TOKEN alone is let through to POST /staff/people/{id}/delete, and to nothing else).
Headers
Section titled “Headers”Seconds until asking again is worth it (300 while the switch is on).