v3.9.0
release: v3.9.0 - Browser mode (#119)
Slothlet v3.9.0 Changelog
Release Date: May 2026
Release Type: Minor
Branch: release/3.9.0
Overview
Version 3.9.0 introduces browser / worker mode — slothlet can now compose and run an api tree in environments without a filesystem or Node built-ins (browsers, web workers, Electron renderers). Instead of scanning directories at runtime, browser mode consumes a pre-generated manifest describing the api folder structure and loads each leaf through a resolvable module specifier rather than a file:// path.
import slothlet from "@cldmv/slothlet";
import { generateManifest } from "@cldmv/slothlet/helpers/generate-manifest";
// Build time (Node): snapshot the api folder structure once.
const manifest = await generateManifest("./src/api");
// Runtime (browser): no fs — the manifest stands in for directory scanning.
const api = await slothlet({ base: "./src/api", manifest });Browser mode is auto-detected: providing a manifest (or an explicit platform: "browser") selects it. Because a browser has no node:async_hooks, browser mode runs the live-binding context runtime instead of AsyncLocalStorage, and all node:* imports are deferred so a bundler never pulls them into the browser graph.
This release also splits the old env option into two orthogonal concerns — platform (the execution target) and env (the process.env snapshot) — and begins the dir → base migration (the old dir option still works, now deprecated in favor of base).
Compatibility. No breaking changes for Node hosts. dir continues to work as a deprecated alias for base; platform / manifest are additive and default to Node behavior when absent. Hosts that don't pass a manifest see no change.
🚀 New Features
Browser / worker mode (platform: "browser" + manifest)
slothlet's directory scanner and file:// loader are Node-only. Browser mode replaces both: the manifest supplies the folder structure the scanner would have discovered, and a module-specifier resolver turns each manifest entry into something the host environment can import() (a bundler URL, an import-map specifier, an https:// URL, etc.).
platform: "browser"forces browser behavior; omitting it auto-detects (browserwhen amanifestis present or no Nodeprocessexists,nodeotherwise).manifest— a{ files, directories }structure produced bygenerateManifest(). Required in browser mode.- The live-binding context runtime is forced in a browser (no
AsyncLocalStorage);self/context/instanceIDstill work for the single-flow case. - The
process.envsnapshot is skipped entirely in browser mode — noprocessaccess occurs.
generateManifest() helper
A new Node-side, build-time helper at @cldmv/slothlet/helpers/generate-manifest. Walks an api folder once and emits the manifest browser mode consumes at runtime, so the structure is fixed at build time and no filesystem access is needed in the browser.
resolveModuleSpecifier — custom module resolution
config.resolveModuleSpecifier({ path, name, fullName }) lets the host map each manifest entry to an importable specifier. The default resolver resolves relative to base, automatically converting a plain filesystem base (e.g. /path/to/api) into a file:// URL so the same config shape works in Node without manual prefixing.
platform vs env split
The previous single env option conflated "which environment am I targeting" with "what is my process.env." These are now separate: platform controls whether filesystem-dependent code paths run, and env remains the process.env snapshot. They are independent — a Node host can pass an env snapshot without changing platform behavior.
suppressFixes opt-out (C03)
A new suppressFixes config flag opts out of the C03 multi-default-export bug-fix behavior for hosts that depended on the older flattening semantics, providing a migration escape hatch.
🐛 Bug Fixes
- Defer
node:*imports so the browser bundle never loads them. Node built-ins are now resolved lazily through the platform layer, so a browser/worker bundle never statically pulls innode:fs/node:path/node:url/node:async_hooks. - C03 multi-default flatten + hoisted-wrapper apiPath. Wired the
flattenToRootconsumer for C03 multi-default files and included the category prefix in the hoisted-wrapperapiPathso flattened paths resolve correctly. - Security hardening flagged by CodeQL. Switched the no-package-root cache fallback to
mkdtemp(CWE-377 insecure temp directory) and closed file-system race (TOCTOU / CWE-367) windows. - Types. Entry points now reference the
SlothletOptionstypedef so editors surface the full option set.
🌐 Internationalization
A script-purity audit was added for non-Latin locales, and English leakage it flagged was translated across the shipped locale set. Locale files were normalized in the process.
Upgrade notes
No action required for Node hosts. To adopt browser mode:
- At build time (Node), call
generateManifest("./your/api")and ship the result with your bundle. - At runtime (browser/worker), pass
{ base: "./your/api", manifest }toslothlet({...}). Browser mode is selected automatically. - If your environment needs custom resolution (import maps, remote URLs), supply
config.resolveModuleSpecifier.
dir still works but is deprecated — prefer base. The env option is unchanged for Node; the new platform option only matters if you want to force a target explicitly.