Practical tips to improve your Signal K plugin's registry score. Most fixes take less than 5 minutes.
| Tier | Points | How to pass |
|---|---|---|
| Install | 20 | npm install --ignore-scripts succeeds |
| Load | 15 | Module exports a function that returns {id, name, start, stop} |
| Activate | 15 | start(config) completes without error — config is populated from your schema defaults |
| Schema | 5 | plugin.schema returns a JSON Schema object |
| Tests | 25 | npm test passes (biggest single tier — see below) |
| Security | 20 | npm audit finds no high or critical vulnerabilities |
npm audit
npm audit fix
Most issues come from transitive dependencies. Update your direct dependencies first. If a vulnerability is in a deep transitive dep you don't control, consider whether you really need that dependency.
Every property in your schema should have a default value. The registry extracts these and passes them to start(). If your plugin crashes without them, it loses 15 points.
schema: {
type: 'object',
properties: {
interval: {
type: 'number',
title: 'Update interval (seconds)',
default: 60
}
}
}
See Plugin Configuration & Schemas for full details.
Even with schema defaults, defensive coding helps:
start(config) {
const interval = config.interval ?? 60
const items = config.items || []
}
The registry clones your source repo and runs npm test. The easiest approach uses Node's built-in test runner — zero dependencies needed.
Create test/plugin.test.ts:
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import pluginFactory from '../src/index'
describe('plugin', () => {
const app = { debug: () => {}, error: () => {} } as any
const plugin = pluginFactory(app)
it('has required interface', () => {
assert.equal(typeof plugin.start, 'function')
assert.equal(typeof plugin.stop, 'function')
assert.ok(plugin.id)
})
it('starts and stops without error', () => {
plugin.start({}, () => {})
plugin.stop()
})
})
Add to package.json:
"scripts": {
"build": "tsc",
"test": "tsc && node --test dist/test/plugin.test.js"
}
The registry clones your source repo, runs npm install and npm run build, then npm test — so typescript from your devDependencies is available.
Create test/plugin.test.js:
const { describe, it } = require('node:test')
const assert = require('node:assert/strict')
const pluginFactory = require('../plugin/index.js')
describe('plugin', () => {
const app = { debug: () => {}, error: () => {} }
const plugin = pluginFactory(app)
it('has required interface', () => {
assert.equal(typeof plugin.start, 'function')
assert.equal(typeof plugin.stop, 'function')
assert.ok(plugin.id)
})
it('starts and stops without error', () => {
plugin.start({}, () => {})
plugin.stop()
})
})
Add to package.json:
"scripts": {
"test": "node --test test/plugin.test.js"
}
Why node:test? Published npm packages don't include devDependencies, so jest/mocha won't be available when the registry installs your plugin. The registry clones your source repo to run tests, but node:test is built into Node and always available.
Your start() assumes config has nested objects that don't exist yet. Add default values to nested properties in your schema, or use optional chaining (config.options?.speed ?? 5).
Your test runner (jest, mocha, vitest) isn't installed because devDependencies aren't available. Switch to node:test (built-in) or ensure the test command works after a fresh npm install.
Run npm audit locally. Usually it's a transitive dependency. Try npm audit fix or update the parent dependency that pulls it in.
The registry retests when a new version is published to npm. Bump your version and publish. Alternatively, results older than 7 days are automatically retested on the nightly run.