Packages
Fairyfly installs pure-JS ESM packages into node_modules/, pinned in ff.json and locked in ff.lock (v2, with integrity hashes). Dependencies can come from npm or directly from GitHub, Codeberg, or a .tar.gz URL. Two commands cover everything: ff imprint adds, ff sever removes.
Quick look
Installing: ff imprint
Only ESM-compatible packages pass the pure-ESM gate — CommonJS-only packages are rejected at install time.
Note: Commit both
ff.jsonandff.lock. The lock carries integrity hashes; without it a fresh clone re-resolves and may drift.
Direct sources: GitHub, Codeberg, archives
ff imprint also accepts a repository archive instead of an npm package:
Omit #ref to use HEAD (the forge's default branch).
The archive must contain a package.json at its root. That file supplies the
package name (the key written to ff.json) and version, plus its
dependencies / optionalDependencies, which are resolved from npm as usual.
ff records the normalized source in ff.json and pins the exact archive URL
plus a computed SHA-512 integrity hash in ff.lock. Reinstalling from the lock
verifies that hash; if a branch moved since the lock was written, the install
fails instead of silently changing code.
Note: Direct sources are fetched anonymously over HTTPS. Private repositories, authentication, monorepo subdirectories, and build scripts are not supported: the archive root must already contain the runnable package. Prefer a tag or commit in
#reffor reproducible installs;HEADand branch names follow the remote.
Importing: bare vs relative
After install, import by bare specifier (resolved through node_modules/):
Your own files use relative imports — extensions are optional (omitting
one probes X.js, then X/index.js):
Note: The probe only tries
X.jsandX/index.js. Non-.jstargets (./lib.mjs) must be written out.
Removing: ff sever
Removal is scope-aware (ff sever @scope/pkg@1.0 strips to @scope/pkg) and pnpm-style: shared packages still needed by remaining deps stay; only newly-unreachable ones are pruned.
Practical example: markdown blog
CI pattern
Troubleshooting
Install rejects my package — it's likely CommonJS-only. Only pure-JS ESM passes the gate; look for an ESM build or replacement.
Direct source says "no package.json at the archive root" — point the URL at a single package. Monorepo subdirectories are not supported.
Direct source fails the integrity check — the branch or tag moved since ff.lock was written. Re-run ff imprint <source> to accept the new archive, or pin a tag/commit.
Cannot find module 'x' — you forgot ff imprint x, or ran from the wrong directory (resolution is relative to cwd's node_modules/).
Lock keeps changing — someone runs bare ff imprint without a lock present, which re-resolves. Commit ff.lock and use no-args ff imprint for rebuilds.
Stale dep after sever — with names, only unreachable packages prune. If it's still needed transitively, it stays — check ff.json for who still requires it. For a clean slate: ff sever --force, then ff imprint.
