Skip to content

Plugins and libraries

How-to

Read first: getting-started/scripts, getting-started/background-jobs

This page shows you how to add the plugins your scripts need to your project, load a library from a script, and decide what each plugin is allowed to do.

You’ll add a library with hpy happyview add, see what it writes to happyview.toml and happyview.lock, use the library in a query script, and read the capabilities it asks for. The examples use the happyview.db library and the xyz.statusphere.status collection.

HappyView runs your scripts inside WebAssembly plugins. Two kinds matter for scripts:

  • An interpreter runs scripts in one language. The official ones are happyview-lua, happyview-javascript and happyview-typescript. A script’s language is whichever interpreter claims its file extension.
  • A library gives scripts a set of functions under a happyview.* name, such as happyview.db for reading indexed records or happyview.jobs for queuing background jobs. A script loads a library with require in Lua or import in JavaScript and TypeScript.

HappyView itself ships with no interpreter and no library. Everything a script can reach beyond the built-in internal.* modules comes from a library your project adds.

hpy happyview create already added the interpreters you chose, Lua by default. To add a library, or another interpreter, run hpy happyview add from the project root with the library’s name or the interpreter’s language:

Terminal window
hpy happyview add happyview.db # a library, by its namespace
hpy happyview add happyview-db # the same library, by its project name
hpy happyview add javascript # an interpreter, by its language

hpy looks the name up in the registry and works out whether it’s an interpreter or a library. If a name is both, pass --interpreter or --library. If several registry projects answer to one name, hpy asks which one you mean, or you can name it with --project.

Each plugin publishes up to three channels: latest, beta and alpha. With no option, hpy follows latest, or the first of beta and alpha the plugin publishes if it has no latest. To choose:

Terminal window
hpy happyview add happyview.db --channel alpha # follow a channel: latest, beta or alpha
hpy happyview add happyview.db --version 1.2.0 # pin one exact version

--channel accepts only latest, beta and alpha. You can’t pass both --channel and --version.

add writes an entry to happyview.toml. Interpreters go under [interpreters], keyed by language. Libraries go under [libraries], keyed by namespace:

[interpreters]
lua = { did = "did:plc:mo7vq4jbioxwafy5zkcc3eux", name = "happyview-lua", channel = "alpha" }
[libraries]
"happyview.db" = { did = "did:plc:mo7vq4jbioxwafy5zkcc3eux", name = "happyview-db", channel = "alpha" }

Each entry names the registry project by its publisher’s DID and its name, and follows either a channel or one exact version.

add then resolves every entry to an exact version, downloads each plugin, and writes happyview.lock. The lock pins the version and the CIDs that the downloaded files must match. Commit it, and don’t edit it by hand:

# Written by hpy from happyview.toml. Commit it; don't edit it by hand.
version = 2
[happyview]
version = "3.0.0"
[[plugin]]
id = "happyview-db"
kind = "library"
keys = ["happyview.db"]
project = "at://did:plc:mo7vq4jbioxwafy5zkcc3eux/software.release.project/happyview-db"
version = "1.2.0"
channel = "alpha"
version_uri = "at://did:plc:mo7vq4jbioxwafy5zkcc3eux/software.release.version/happyview-db.1.2.0"
# manifest_cid, package_cid and manifest_blob_cid follow
dependencies = []

A channel moving on to a newer version doesn’t change the lock. Your project keeps the locked version until you run hpy happyview add for that plugin again. Run hpy happyview add with no plugin names to bring the lock up to date with happyview.toml and download it.

If anything fails, such as a name the registry doesn’t know or a download that doesn’t match, add changes neither file.

hpy happyview dev puts the locked plugins in place before it starts HappyView. A running dev doesn’t watch the plugins directory, so stop it and start it again after add.

hpy happyview deploy makes an instance match your project. Plugins come first: it installs each plugin in happyview.lock at its locked version, or moves an installed one to that version. Then it sends your lexicons, scripts and modules. Sign in to the instance first with hpy happyview login.

  • A deploy installs exactly what the lock pins. If happyview.lock doesn’t match happyview.toml, the deploy is refused and tells you to run hpy happyview add.
  • It lists the plugin steps, such as install happyview-db 1.2.0, and asks before it changes anything. --yes skips the question. --dry-run shows the changes without making them.
  • A plugin installed on the instance but missing from the lock is kept. Pass --prune-plugins to uninstall it.
  • The account you deploy with needs the plugins:create permission to install plugins.

Once happyview.db is in your project, any script can load it. This query script lists one account’s statuses, newest first:

local db = require("happyview.db")
local log = require("internal.logging")
function handle(input, ctx)
local page = db.records("xyz.statusphere.status")
:did(input.did)
:sort("createdAt", "desc")
:limit(input.limit)
:run()
log.info("statuses read", { did = input.did, count = #page.records })
return { statuses = page.records, cursor = page.cursor }
end

require looks for a built-in internal.* module first, then for an installed library with that name. In JavaScript and TypeScript, every library call returns a promise, so await it. In Lua, a library call returns its result directly.

If the library isn’t installed, the script fails when it runs, with an error such as module 'happyview.db' not found -- is the 'happyview.db' library plugin installed?. The build doesn’t check library names, so hpy happyview check won’t catch this. Add the library before you rely on it.

A library that fails raises an error in your script. The message carries an error code, such as BAD_INPUT or NO_SESSION. Each library’s README lists its codes. Catch the error when you expect it:

local db = require("happyview.db")
function handle(input, ctx)
local ok, result = pcall(db.get, input.uri)
if not ok then
return { error = tostring(result) }
end
return { record = result }
end

The full list of libraries, with what each one does, is in Libraries.

Every plugin declares the capabilities it needs, and HappyView grants it exactly those and nothing more. A capability is a group of things the plugin may do through HappyView, such as records:read to look up indexed records or database:write to run any SQL.

Some examples from the official plugins:

PluginCapabilities
happyview-lua, happyview-javascript, happyview-typescriptlibrary:call, script:host, script:test, wasi:clock, wasi:random, wasi:stdio
happyview-dbrecords:read
happyview-sqldatabase:read, database:write
happyview-jobsjobs:create, jobs:read, jobs:read_any
happyview-httpnetwork:request:unrestricted

A library’s capabilities bound what every script can do through it. With happyview-sql installed, any script can run any SQL statement. With happyview-http, any script can call any host. Add only the libraries your scripts use.

An interpreter’s library:call lets scripts call installed libraries. Each library runs with its own capabilities, not the interpreter’s.

Whoever installs a plugin on an instance agrees to the capabilities it asks for. How that happens depends on the way it’s installed:

  • hpy happyview deploy doesn’t list capabilities, and the instance grants each plugin every capability its manifest declares. Adding a plugin to happyview.lock is where you agree to them, so read them before you add it.
  • The dashboard shows them on the install screen’s Permissions tab, with a risk level for each (Low risk, Medium risk, High risk or Critical) and a sentence that says what it allows. Installing grants what is shown there. When an update or a channel switch asks for a capability the installed version doesn’t have, the dashboard shows This version asks for new permissions. Check Allow these permissions to go ahead. If you don’t, the installed version keeps running.
  • HAPPYVIEW_DEFAULT_PLUGINS: naming a plugin there consents to every capability its manifest declares.

Some library calls also need a user to act as, whatever capabilities the library has. A library acts as ctx.caller_did, and some scripts have none.

The same holds for an anonymous request to a query. Record scripts act as the record’s author, so they can queue jobs. See Background jobs.