# 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. ```js 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 (`browser` when a `manifest` is present or no Node `process` exists, `node` otherwise). - `manifest` — a `{ files, directories }` structure produced by `generateManifest()`. Required in browser mode. - The live-binding context runtime is forced in a browser (no `AsyncLocalStorage`); `self` / `context` / `instanceID` still work for the single-flow case. - The `process.env` snapshot is skipped entirely in browser mode — no `process` access 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 in `node:fs` / `node:path` / `node:url` / `node:async_hooks`. - **C03 multi-default flatten + hoisted-wrapper apiPath.** Wired the `flattenToRoot` consumer for C03 multi-default files and included the category prefix in the hoisted-wrapper `apiPath` so 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 `SlothletOptions` typedef 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: 1. At build time (Node), call `generateManifest("./your/api")` and ship the result with your bundle. 2. At runtime (browser/worker), pass `{ base: "./your/api", manifest }` to `slothlet({...})`. Browser mode is selected automatically. 3. 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.