Liking cljdoc? Tell your friends :D

koine.fs

Filesystem, portable.

Note file-seq is a trap: it resolves on BOTH hosts but takes different argument types — a java.io.File on the JVM, a string path on cljgo. A name that resolves everywhere is not thereby portable, which is why directory traversal lives behind this seam.

Filesystem, portable.

Note `file-seq` is a trap: it resolves on BOTH hosts but takes different
argument types — a java.io.File on the JVM, a string path on cljgo. A name
that resolves everywhere is not thereby portable, which is why directory
traversal lives behind this seam.
raw docstring

delete!clj/s

(delete! path)

Delete the file or EMPTY directory at path. Returns nil. Not an error if it is already absent — deleting is a statement about the end state, and both hosts agree only if koine says so.

Delete the file or EMPTY directory at `path`. Returns nil. Not an error if
it is already absent — deleting is a statement about the end state, and both
hosts agree only if koine says so.
sourceraw docstring

delete-tree!clj/s

(delete-tree! path)

Recursively delete path and everything under it (rm -rf). Returns nil. Not an error if it is already absent.

No reader conditional: both hosts have a native recursive delete, and using neither is the better trade. Deepest-first over list-tree is the same traversal koine already guarantees, so the ORDER of removal is koine's and cannot differ per host — and a bug here deletes the wrong thing, which is the last place to want two implementations.

Recursively delete `path` and everything under it (`rm -rf`). Returns nil.
Not an error if it is already absent.

No reader conditional: both hosts have a native recursive delete, and using
neither is the better trade. Deepest-first over `list-tree` is the same
traversal koine already guarantees, so the ORDER of removal is koine's and
cannot differ per host — and a bug here deletes the wrong thing, which is the
last place to want two implementations.
sourceraw docstring

directory?clj/s

(directory? path)
source

exists?clj/s

(exists? path)
source

find-filesclj/s

(find-files root suffix)

Every file under root whose path ends with suffix, SORTED.

Sorted because skill discovery must be deterministic across hosts — the underlying traversal order is not guaranteed to match.

Every file under `root` whose path ends with `suffix`, SORTED.

Sorted because skill discovery must be deterministic across hosts — the
underlying traversal order is not guaranteed to match.
sourceraw docstring

list-treeclj/s

(list-tree root)

Every path under root, recursively, as strings — files and directories. Order is unspecified per host; callers that need determinism must sort.

Every path under `root`, recursively, as strings — files and directories.
Order is unspecified per host; callers that need determinism must sort.
sourceraw docstring

mkdirs!clj/s

(mkdirs! path)

Create directory path and every missing parent (mkdir -p). Returns path. Not an error if the directory already exists.

It IS an error if path exists and is not a directory — you asked for a directory and did not get one, and the next write would go somewhere you did not intend. The hosts disagreed here and koine picks the loud answer: cljgo already threw, while the JVM's .mkdirs returns false and koine was discarding it, so the call looked like it had succeeded. Found by probing the states fs_check never entered, 2026-07-31.

Create directory `path` and every missing parent (`mkdir -p`). Returns
`path`. Not an error if the directory already exists.

It IS an error if `path` exists and is not a directory — you asked for a
directory and did not get one, and the next write would go somewhere you did
not intend. The hosts disagreed here and koine picks the loud answer: cljgo
already threw, while the JVM's `.mkdirs` returns false and koine was
discarding it, so the call looked like it had succeeded. Found by probing the
states `fs_check` never entered, 2026-07-31.
sourceraw docstring

read-bytesclj/s

(read-bytes path)

The whole file at path as a byte array. Use this, never read-file, for anything that is not text — the text route is lossy for non-UTF-8 bytes.

The whole file at `path` as a byte array. Use this, never `read-file`, for
anything that is not text — the text route is lossy for non-UTF-8 bytes.
sourceraw docstring

read-fileclj/s

(read-file path)

The whole file at path as a string (UTF-8).

The whole file at `path` as a string (UTF-8).
sourceraw docstring

real-pathclj/s

(real-path path)

path with every symlink resolved, as an absolute, cleaned path. Throws if path does not exist.

Unlike making a path absolute, this TOUCHES THE FILESYSTEM — that is the whole point, and it is why cljg.io/absolute (Go's filepath.Abs) is not a substitute: Abs cleans a path lexically and never follows a link, while returning something that looks canonical.

Use it to canonicalise before comparing two paths, and to guard a directory walk against a symlink CYCLE:

(loop [[d & more] roots seen #{}]
  (let [c (fs/real-path d)]
    (if (contains? seen c)
      (recur more seen)            ; already been here — do not descend
      (recur (concat more (fs/list-tree d)) (conj seen c)))))

Without it there is no cycle guard at all, and ln -s ../.. loop makes a walk run forever — list-tree FOLLOWS directory symlinks on both hosts, verified.

Note the result may differ from the input in more than the links: macOS resolves /tmp to /private/tmp, for instance. That is canonicalisation doing its job — compare canonical paths to canonical paths, never to raw input.

A MISSING path throws on both hosts, and koine has to insist on that: cljgo throws by itself, while the JVM's .getCanonicalPath happily cleans a path that is not there and hands back something that LOOKS canonical. Returning that would be the worst outcome — a caller cannot tell a resolved path from an unresolved one, so a cycle guard would silently stop guarding.

`path` with every symlink resolved, as an absolute, cleaned path. Throws if
`path` does not exist.

Unlike making a path absolute, this TOUCHES THE FILESYSTEM — that is the whole
point, and it is why `cljg.io/absolute` (Go's `filepath.Abs`) is not a
substitute: `Abs` cleans a path lexically and never follows a link, while
returning something that looks canonical.

Use it to canonicalise before comparing two paths, and to guard a directory
walk against a symlink CYCLE:

    (loop [[d & more] roots seen #{}]
      (let [c (fs/real-path d)]
        (if (contains? seen c)
          (recur more seen)            ; already been here — do not descend
          (recur (concat more (fs/list-tree d)) (conj seen c)))))

Without it there is no cycle guard at all, and `ln -s ../.. loop` makes a walk
run forever — `list-tree` FOLLOWS directory symlinks on both hosts, verified.

Note the result may differ from the input in more than the links: macOS
resolves /tmp to /private/tmp, for instance. That is canonicalisation doing
its job — compare canonical paths to canonical paths, never to raw input.

A MISSING path throws on both hosts, and koine has to insist on that: cljgo
throws by itself, while the JVM's `.getCanonicalPath` happily cleans a path
that is not there and hands back something that LOOKS canonical. Returning
that would be the worst outcome — a caller cannot tell a resolved path from an
unresolved one, so a cycle guard would silently stop guarding.
sourceraw docstring

temp-dir!clj/s

(temp-dir!)
(temp-dir! prefix)

Create a fresh temporary directory and return its path.

Each call returns a NEW directory — the caller owns it and should delete-tree! it. prefix only shapes the name; never rely on the exact path, which differs per host and per OS.

Create a fresh temporary directory and return its path.

Each call returns a NEW directory — the caller owns it and should
`delete-tree!` it. `prefix` only shapes the name; never rely on the exact
path, which differs per host and per OS.
sourceraw docstring

write-bytesclj/s

(write-bytes path bs)

Write byte array bs to path, replacing any existing file. Returns nil.

Write byte array `bs` to `path`, replacing any existing file. Returns nil.
sourceraw docstring

write-fileclj/s

(write-file path s)

Write string s to path, replacing any existing file. Returns nil.

Write string `s` to `path`, replacing any existing file. Returns nil.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close