Your first plugin
Create a minimal plugin that listens for a new order.
This example uses plain JavaScript modules and the runtime context supplied by the kernel. It does not require a scaffolding CLI.
Create the package
Create a directory with these files:
hello-plugin/
package.json
index.js
Use this package.json:
{
"name": "@sellforge-plugin/hello",
"version": "0.1.0",
"type": "module",
"main": "./index.js",
"files": ["index.js", "migrations"],
"sellforge": {
"displayName": "Hello Sellforge",
"description": "Logs newly placed orders."
}
}
Register an event handler
Create index.js:
export default {
name: '@sellforge-plugin/hello',
async register(ctx) {
ctx.hooks.on('order.placed', async ({ orderId }) => {
ctx.logger.info('A new order was placed', { orderId })
})
return {
ping() {
return 'pong'
},
}
},
}
The loader supplies ctx. The returned ping service is available in the internal registry; it is not automatically exposed as an HTTP endpoint.
Test the registration locally
Create test.mjs alongside index.js and run it with node test.mjs:
import assert from 'node:assert/strict'
import plugin from './index.js'
const handlers = new Map()
const messages = []
const services = await plugin.register({
hooks: { on: (name, handler) => handlers.set(name, handler) },
logger: { info: (message, meta) => messages.push({ message, meta }) },
})
assert.equal(services.ping(), 'pong')
assert.ok(handlers.has('order.placed'))
await handlers.get('order.placed')({ orderId: 'ord_test' })
assert.equal(messages[0].meta.orderId, 'ord_test')
console.log('Plugin registration and handler passed.')
Load it in a development kernel
Make the package resolvable from the kernel's Node.js environment, for example by adding a local package dependency to a development kernel. The install endpoint registers an already available package; it does not download one from npm.
Then use the platform-authorized installation flow to install and enable it. Test against a development shop before enabling the package in another environment.
Next steps
Add configuration through the manifest, learn the hook payloads, or create plugin-owned tables with migrations.
Found something unclear? Let us know