Skip to content
Warlock.js v4.7.0

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.

  1. Import the facade.

    One named import gets you the whole toolkit:

    import { fs } from "@warlock.js/fs";

    fs.files handles files, fs.dirs handles directories, and fs.file(path) / fs.dir(path) give you lazy handle objects. That’s the entire surface you need to remember.

  2. Write some JSON.

    Save an object as pretty-printed JSON — no manual JSON.stringify, no mkdir -p dance:

    await fs.files.putJson("./data/config/app.json", {
    name: "warlock",
    version: "1.0.0",
    });

    The ./data/config folders didn’t exist a second ago. putJson created them for you before writing.

  3. 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");
    // ^? AppConfig

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

  4. Grab a handle and inspect it.

    When you’re going to touch the same file more than once, bind it to a File handle 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 disk

    file.name is instant — handles do zero IO until you call an async method like .size(). From here file.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.