Skip to content

Keep the folder size index current from the write, not from the sweep - #443

Merged
vikramsoni2 merged 2 commits into
nxzai:mainfrom
cerede2000:p3-11
Sep 26, 2026
Merged

vikramsoni2 merged 2 commits into
nxzai:mainfrom
cerede2000:p3-11

Conversation

@cerede2000

Copy link
Copy Markdown

P3-11 of phase 3 (see #373). Stacked on #433–#442.

The index is never told about a write the application just made

The folder size index learns a size three ways: a first pass, a watcher, and an adaptive
sweep that walks folders again looking for changes. Nothing tells it about a write the
application itself performed — and that is the one case where it needs no traversal at all,
because the exact number of bytes is already in hand.

So after an upload, a save in the editor, a new folder or a document created from a
template, the size shown for the folder is the one from before, until a sweep happens to
come round to it. On a large volume that is minutes.

The write hands over its own delta

services/folderSizeHooks.js takes the number each operation already has and propagates it
to the folder and its ancestors. Four call sites here — the upload, the editor save, a new
folder, a document created with contents — and every hook is best-effort: gated on the
feature being on, and swallowing its own errors, because a folder size is not worth failing
a write for.

The editor save reads what the file weighed before it wrote, so the folder gains the
difference rather than the whole of the new file. That is why the hook takes two numbers.

folderSizeIndex also records which mode the index was built in, and folderSizeManager
measures again when it starts in a different one: sizes measured as apparent and sizes
measured on disk are not the same numbers, and keeping the other mode's answers showed a
total nobody could reconcile with anything.

Also here

frontend/.eslintrc.cjs gains env: { browser, es2022 }. ecmaVersion: 'latest' sets what
syntax is allowed, not what exists at run time, so globalThis — standard since ES2020, and
what a store reaches for to touch a timer the browser owns — read as an undefined name in
nine places. Nine errors fewer in npm run lint, and it would have applied to any file that
reached for it.

Checks

81 tests across folder-size-hooks, folder-size-flush, folder-size-disabled,
folder-size-mode-change, folderSizeIndexer and the folderSize route.

Making onFileWritten say nothing — the behaviour being replaced — turns 34 of them
red.

  • Whole backend suite: 2 545 passed, 2 failed — the two that fail on main alone.
  • npm run format:check reports 21 files against main's 22: one of them is a file this
    batch rewrites, which comes back clean.
  • Frontend builds; backend loads.

The hooks belong in three more places — the two office editors and the archive extraction —
and those call sites travel with their own batches.

Benjy added 2 commits September 26, 2026 20:17
`exifr` was last published in 2022. It is the one thing in the image that opens a file
somebody else wrote using code nobody maintains any more, and it is loaded lazily in the
metadata route precisely because nobody was sure of it.

`exif-reader` replaces it: maintained alongside sharp, and it does nothing but walk a
TIFF-shaped block with a bounds check on every read. The block itself needs no file
access — sharp already opens the image to report its dimensions and hands the raw EXIF
block back with them — so a photograph's details now come from one pass instead of two,
and `exifr` leaves the dependencies.

`utils/exifDetails.js` holds what the block is read for: when the picture was taken, the
camera and lens, the exposure, the orientation, the place. Written as its own file because
the metadata route is about what kind of thing a file is, and this is about what a
photograph says of itself.

## Checks

`exif-details.test.js`, 20 tests over the reading itself — a block that is not EXIF, a
truncated one, dates in the three shapes cameras write them, a rational that divides by
zero, coordinates in both hemispheres. Making the reader answer nothing turns six of them
red.

`metadata.test.js`, 13 tests through the route.

Whole backend suite: 2 509 passed, 2 failed — the two that fail on `main` on its own.

`capabilities.js`, `documentText.js` and `pdfTextExtract.js` were in this batch at first
and are not any more: probing which optional tools the machine has, and reading a
document's text, are their own ground, and bringing them here turned three of `main`'s own
tests red.
The index has three ways of learning a folder's size: a first pass, a watcher, and an
adaptive sweep that walks folders again looking for changes. Nothing told it about a write
the application itself had just made — and the application knows the exact number of bytes
at that moment, which is the one case where no traversal is needed at all.

So after an upload, a save in the editor, a new folder or a document created from a
template, the size shown for the folder was the one from before, until a sweep happened to
come round to it. On a large volume that is minutes.

`services/folderSizeHooks.js` takes the delta each operation already has and propagates it
to the folder and its ancestors. Four call sites here: the upload, the editor save, a new
folder, and a document created with contents. Every hook is best-effort — gated on the
feature being on, and swallowing its own errors — because a folder size is not worth
failing a write for.

The editor save reads what the file weighed before it wrote, so the folder gains the
difference rather than the whole of the new file. That is the whole reason the hook takes
two numbers.

`folderSizeIndex` also records which mode the index was built in, and `folderSizeManager`
measures again when it starts in a different one: sizes measured as "apparent" and sizes
measured on disk are not the same numbers, and keeping the other mode's answers was
showing a total nobody could reconcile with anything.

## Checks

81 tests across `folder-size-hooks`, `folder-size-flush`, `folder-size-disabled`,
`folder-size-mode-change`, `folderSizeIndexer` and the `folderSize` route. Making
`onFileWritten` say nothing — the behaviour being replaced — turns 34 of them red.

Whole backend suite: 2 545 passed, 2 failed, the two that fail on `main` on its own.

`frontend/.eslintrc.cjs` gains `env: { browser, es2022 }`. `ecmaVersion: 'latest'` sets
what syntax is allowed and not what exists at run time, so `globalThis` — standard since
ES2020 — read as an undefined name in nine places. That is nine errors fewer in
`npm run lint`, and it would have applied to any file that reached for it.

The hooks belong in three more places — the two office editors and the archive extraction —
and those call sites travel with their own batches.
cerede2000 pushed a commit to cerede2000/NextExplorer that referenced this pull request Sep 26, 2026
The batch is reversed, so its findings move from PORT to DONE in the same commit that
sends them. What is left to reverse is whatever `scripts/parity.mjs` still reports.
@vikramsoni2
vikramsoni2 merged commit 5c3d49b into nxzai:main Sep 26, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants