npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@venn-lang/fs

v0.9.0

Published

The fs namespace: reading a file whole, writing one whole, and asking what is on the disk.

Readme

@venn-lang/fs

The fs namespace: reading a file whole, writing one whole, and asking what is on the disk.

Until this package there were twenty-three namespaces and not one verb that read or wrote a file. A program that wanted to summarise a JSON file could not open it: it read standard input, printed to standard output, and left the shell to say which file it meant. That is a filter wearing the costume of a program, and it stops being possible the moment the program is run by anything but a shell.

Four verbs, all of them over the file system port the language already had. Nothing here opens a handle, seeks, or keeps a file open across a statement.

Install

Part of the stdlib the venn CLI and the language server load:

import { fs } from "venn/fs"

The plugin declares requires: ["fs"]. A host with no disk refuses it once, at load time, with VN2010, rather than letting a program get halfway and fail on its first read.

Usage

import { fs } from "venn/fs"
import { json } from "venn/json"
import { fmt } from "venn/fmt"

const doc = try json.parse(fs.read("inventory.json")) else null
if doc == null {
  fail "inventory.json is not JSON"
}

const summary = {
  total: doc.items.sumBy(i => i.qty * i.price),
  outOfStock: doc.items.filter(i => i.qty == 0).map(i => i.name)
}

fs.write("summary.json", fmt.json(summary))
print "wrote summary.json"

Verbs

| Verb | Signature | What it does | | --- | --- | --- | | fs.read | (string) -> string | The whole file as text, read as UTF-8. | | fs.write | (string, string) -> void | The text as the whole file. Missing parents are made. | | fs.exists | (string) -> bool | Whether there is anything at that path. | | fs.list | (string) -> list of { name, directory } | What a directory holds, one level deep. |

A file that is not there is a failure, not a value

fs.read refuses a file that is not there. It does not answer null, because null in this language means there is no value at this position, and a file that is not on the disk is a fact about the world rather than an empty position. So it is caught the way every other failure is:

import { fs } from "venn/fs"

try {
  print fs.read("nowhere.json")
} catch e {
  print "${e.code}: ${e.message}"
}

That prints VN8010: File not found: "nowhere.json". The code is the one the file system port has always raised, and this namespace neither renames it nor writes a second sentence about it.

When a default will do, the expression form is shorter and needs no binding:

import { fs } from "venn/fs"

const settings = try fs.read("settings.json") else "{}"
print settings

And when the question is genuinely a question rather than a failure, ask it:

import { fs } from "venn/fs"

if fs.exists("settings.json") {
  print fs.read("settings.json")
}

Walking a directory

fs.list gives one level, each entry naming itself and saying whether it holds more. A deeper walk is that plus a loop, which is a walk the program can see rather than one hidden inside a verb:

import { fs } from "venn/fs"
import { path } from "venn/path"

forEach entry in fs.list("examples") {
  if entry.directory {
    print "dir  ${entry.name}"
  } else {
    print "file ${path.join('examples', entry.name)}"
  }
}

An entry is a name, never a path. Joining it to the directory it came from belongs to venn/path, which knows the separator this host writes and never says it out loud.

Nothing here is a second file system

Every byte goes through venn.port.filesystem, the port that already had two implementations and a conformance suite both are run against: the real disk for the CLI, an in-memory double for tests and for the editor's worker. This package imports no node:* and builds platform: "neutral", which is why the same verbs are available to the language server that is available to the command line.

That is also why the verbs are the shape they are. The port speaks bytes and knows nothing about text, so the UTF-8 in fs.read and fs.write is this package's, done once, through the encoder the SDK already publishes.

What is deliberately absent

| Not here | Why | | --- | --- | | fs.remove, fs.removeAll | Nothing in the programs that asked for a file system deleted one. A delete verb needs a decision about whether it recurses and whether it may leave the directory it was given, and that is a conversation, not a fifth verb. | | fs.append | A second way to write. Read, add, write back covers it until a program shows a log that cannot. | | fs.readBytes, fs.writeBytes | The language has no bytes value, so a binary verb would answer with something a program cannot hold. | | fs.walk | fs.list and a loop are the same walk, and a recursive verb has to decide about links that point at their own parent. | | fs.mkdir | fs.write makes the parents it needs. An empty directory is the only case left and nothing asked for one. | | Size, modified time, permissions | The port has no stat, and widening a conformance contract two implementations are held to is not something to do for a question nobody has asked. |

API

| Export | What it is | | --- | --- | | fsPlugin (also the default export) | The PluginDefinition: namespace fs, requires fs. | | contentActions, questionActions | The ActionDefinitions, in two groups. | | files | The host's disk, out of an ActionContext. | | ENTRY_TYPE | What one fs.list entry is, as the checker sees it. |

See also