Writing principles
Tags, packages and licences
A principle can have a package.json next to its detector.ts.
It says what the principle is about, and which packages the detector imports.
{
"name": "yaml-port",
"private": true,
"type": "module",
"tags": ["yaml", "config"],
"dependencies": {
"yaml": "^2.9.1"
}
}The file is optional.
Without it the principle has no tags and imports nothing but the SDK, node:fs, node:path and its own files.
Tags#
tags says what the principle is about, in words you choose.
- Up to 5 tags, each at most 30 characters.
- Spaces inside a tag are fine:
"null safety". The same tag twice, in any case, counts once. - Tags are shown in lists and on your profile, and help people find the principle in the Library.
- They decide nothing about when it runs. The globs of its handlers do that.
In the browser editor, the Tags field above the files writes them into package.json for you.
For a principle synced from GitHub, the package.json in the repository is the only place they come from.
Visibility, forks and retired#
Three more fields describe the principle itself. They only take effect for a principle synced from GitHub, and all are optional.
{
"name": "avoid-late",
"visibility": "public",
"forkable": false,
"retired": "Superseded by avoid-late-init."
}visibilityis"public"or"private", as the button in the Settings tab of the principle's page, and the agent'sset_visibilityset it.forkableistrueorfalse, whether others may fork it.retiredis the reason people who ran the principle read, at most 500 characters. While it is there the principle is retired and runs for nobody. Take the line out and the next sync brings the principle back.- Each sync applies them to the principle. None is part of a revision, so changing only them releases nothing new.
- What
package.jsonsets, the website does not offer to change. What it leaves out, you set in the Settings tab of the principle's page, as for any other principle.
Packages#
A detector can use a package from npm, for example a parser for the language it judges:
import { onWrite } from "@wellactually/sdk/v4";
import { parse } from "yaml";
onWrite({
globs: ["**/*.yaml"],
*detect(event) {
const config = parse(event.text);
if (typeof config?.port === "number") {
yield {};
}
},
});dependenciesnames every package the detector imports, with a version or a range as in any package.json.devDependenciesare not looked at.- A range such as
^2.9.1is settled when you submit. The agent'spublish_drafttakes the versionnpm installput on your disk. The editor on this site and a sync from GitHub take the newest version in the range. That version is part of the release and does not move until you submit again. - The code of the package is copied into the principle when you submit it, and only the files the detector reaches are taken. After that the registry builds from those files alone. Nothing is installed or fetched when a detector runs, on the registry or on anyone's machine.
Packages from GitHub#
A package does not have to be on npm:
{
"dependencies": {
"dart-kit": "github:passsy/dart-kit#main"
}
}- Add
#branch,#tagor#commitwhen it should not be the default branch. A branch is settled to the commit it is at when you submit. - It may be written in TypeScript, since nothing else builds it.
- The editor on this site reads public repositories on GitHub, and the agent's
publish_draftwhatevernpm installcan reach. - In a repository you sync from GitHub, a package that lives in the same repository is found by its name, with no
github:needed.
What works in the isolate#
Only plain JavaScript. The isolate has no Node and no WebAssembly, so a package that needs either fails at build time with a message that says so.
The detector with its packages may be 1 MB, and it still has 100 ms for every event.
A large parser pays its start each time, so measure with the agent's probe.
Licences#
A released principle carries the code of its packages to everyone who runs it, so the bundle carries their licences too.
At its end are the third-party notices: for each package its name, exact version, licence, source, copyright lines, the full licence text and its NOTICE file.
The page of a revision lists the packages and shows the same notices.
The licence of a package is read from two places that have to agree: the license field of its package.json, and the licence file in the package.
For a package that lives in the same repository as a synced principle, the licence file at the top of the repository counts.
Licences that pass by themselves#
MIT, ISC, BSD-2-Clause, BSD-3-Clause, 0BSD, Apache-2.0, Zlib, Unlicense, CC0-1.0, BlueOak-1.0.0 and Python-2.0 need nothing from you.
- Of
MIT OR GPL-3.0-onlythe branch that passes is taken. - With
AND, every licence named has to pass.
Licences you approve#
Any other licence, a package without one, and a package whose package.json and licence file disagree, need your approval.
You approve the package at its exact version, or at its commit for one from GitHub, and a new version has to be approved again.
- The agent's
publish_draftcannot approve one, so the revision waits as a draft until you approve the package on the principle's page. - The editor on this site asks before it saves.
- The registry takes no draft without the approval, and the page of the revision says what you approved.
What a licence asks of you is said when you approve:
- The GPL and the AGPL put the whole detector bundle under their terms, not only the package. The page of the revision says so to everyone who reads it.
- The LGPL asks that its part stays replaceable by whoever runs the detector, and that its source is available. The notices in the bundle link to it.
The licence of your principle#
Your advice stays yours, all rights reserved, and the principle's own code is MIT. The only choice is whether others may fork it. Licences and forks explains both.
An approved package under the GPL or the AGPL is fine in any principle, public or private: the code is open anyway, and its notice travels in the bundle.
Principles synced from GitHub#
A sync has nobody to ask about a licence. A revision with a licence problem is stored as a draft that is held back: it runs for nobody, its page says why, and what was released before keeps running.
The draft is looked at again when you approve its packages on its page, or push a commit that resolves it. It is then released as any other synced revision is. An approval you gave before still counts for the same package at the same version.