Your first write
Let’s write something to disk and read it back — the whole loop in about
five minutes. Everything goes through the fs facade: one import,
grouped namespaces, no factory and no config.
-
Import the facade.
One named import gets you the whole toolkit:
import { fs } from "@warlock.js/fs";fs.fileshandles files,fs.dirshandles directories, andfs.file(path)/fs.dir(path)give you lazy handle objects. That’s the entire surface you need to remember. -
Write some JSON.
Save an object as pretty-printed JSON — no manual
JSON.stringify, nomkdir -pdance:await fs.files.putJson("./data/config/app.json", {name: "warlock",version: "1.0.0",});The
./data/configfolders didn’t exist a second ago.putJsoncreated them for you before writing. -
Read it back — typed.
Pass a type parameter and you get a real shape back, not
unknown:type AppConfig = { name: string; version: string };const config = await fs.files.getJson<AppConfig>("./data/config/app.json");// ^? AppConfigParsing is handled for you. Want runtime validation too? Pass a
schema(seal, Zod, Valibot — any Standard Schema works) and bad data throws instead of sneaking through. -
Grab a handle and inspect it.
When you’re going to touch the same file more than once, bind it to a
Filehandle and ask it about itself:const file = fs.file("./data/config/app.json");console.log(file.name); // → "app.json"console.log(await file.size()); // → bytes on diskfile.nameis instant — handles do zero IO until you call an async method like.size(). From herefile.get(),file.editJson(),file.copyTo(), and friends all read cleaner than repeating the path.
That’s the core rhythm: write, read typed, handle. Everything else in the library is a variation on these three moves.