Skip to content

Write exports through storage instead of a disk - #47

Merged
davidtorcivia merged 1 commit into
mainfrom
exports-write-through-storage
Aug 4, 2026
Merged

davidtorcivia merged 1 commit into
mainfrom
exports-write-through-storage

Conversation

@davidtorcivia

Copy link
Copy Markdown
Contributor

Exports were the last part of the job pipeline that opened a filesystem. There is no node:fs import in that file at all now.

What changed

  • the finished document goes to storage by key instead of writeFile(blobRoot/...)
  • a PDF reads its stills back through the store instead of readFile(stillsDir/...), and deletes them by key afterwards
  • runDueExport takes one queued export and runs it, claiming by conditional update so two drivers racing cannot both take the same row

Every path involved was already a key. The still a report waits on is requested at a key the export chooses, and the result is stored at a key the job row records. Nothing here needed a filesystem — it just happened to have one, because it was written when there was only one target.

The node pump's export slot now calls the shared runner rather than keeping its own copy of the claim query, so the two cannot drift.

Not yet on the Workers target, and why

Exports still import buildPdfReport and the SVG compositor from @onelight/worker, whose index reaches media.ts and therefore node:child_process. Mounting exports on Workers means lifting those two out of the media graph first — exactly the move made for the rendition planner in #19.

That is a separate piece of work and it is recorded in apps/cf/src/index.ts rather than left to be rediscovered. A comment report requested on that target stays queued until then.

Context

This is the fourth thing that exists on the node target and was never wired for Workers: after the API being shadowed by asset routing, password hashing being impossible, and the sweeps never running. Exports differ in that the code genuinely could not have worked there — it wrote to a disk that does not exist — where the others were wiring.

Gates: typecheck, eslint, prettier, full vitest (1023 passed).

Exports were the last part of the job pipeline that opened a filesystem: the
finished document went to blobRoot/exports/... with writeFile, and a PDF read
its stills back off disk. There is no `node:fs` in that file at all now.

Every path involved was already a key. The still a report waits on is
requested at a key the export chooses, and the result is stored at a key the
job row records, so nothing here needed a filesystem in the first place -- it
just happened to have one.

runDueExport takes one queued export and runs it, claiming by conditional
update so two drivers racing cannot both take the same row. The node pump's
export slot calls it rather than keeping its own copy of the query.

Not yet wired into the Workers cron, and the reason is recorded there: exports
still import the PDF report builder and the SVG compositor from
@onelight/worker, whose index reaches media.ts and therefore
node:child_process. Mounting them on that target means lifting those two out
of the media graph first, exactly as the rendition planner was lifted. A
comment report requested there stays queued until then.
@davidtorcivia
davidtorcivia merged commit 7d76df5 into main Aug 4, 2026
10 checks passed
@davidtorcivia
davidtorcivia deleted the exports-write-through-storage branch August 4, 2026 03:04
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.

1 participant