@arcships/rutis
Write rutis plugins in TypeScript or JavaScript.
import { definePlugin } from '@arcships/rutis'
export default definePlugin({
inject: ['llm'], // services it uses
optional: ['search'], // used when there: ctx.get('search')
config: { type: 'object', properties: { city: { type: 'string' } } },
apply(ctx, config) {
ctx.provide('weather', new Weather(ctx.use('llm'), config.city))
return () => {} // cleanup
},
})
optional names services it can do without: rutis does not wait for them, and ctx.get gives each, or undefined when it was not there as the plugin started (rutis-host 0.20 or later).
What ctx.provide provides reaches rutis by itself, its methods the object's functions, called as async. provides is optional: provides: { weather: { unit: 'sync' } } marks methods sync and tells rutis-host check what the plugin provides before it runs.
Test it on a real host (rutis-host test; @arcships/rutis-runtime and @arcships/rutis-host as dev dependencies, which rutis-host new adds); the plugin is the default export of its module:
import { load } from '@arcships/rutis/testing'
const t = await load(plugin, { config: { city: 'Oslo' }, services: { llm: { ask: async () => 'sunny' } } })
assert.equal(await t.service('weather').today(), 'sunny in Oslo')
await t.unload()
This package has no dependencies. Events (ctx.on, and ctx.emit / parallel / serial / waterfall / bail / filter) are one bus across every plugin, runtime and linked node, dispatched by the host; in a test too (host() puts several plugins on one bus). The host installs the runtime that runs the plugin (@arcships/rutis-runtime for Node, @arcships/rutis-bun for Bun). Start a project with npx @arcships/rutis-host new <name> --lang node (or --lang bun).
Guide (Chinese): docs/guide/typescript-plugin.md.