Skip to content

v3.9.0

Choose a tag to compare

@cldmv-bot cldmv-bot released this 31 May 15:06
· 3 commits to master since this release
v3.9.0
f78c04a

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 dirbase 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.


👥 Contributors