From 1f2a42d55e30e90097dd7fa9dcb2f1eb2eb0362d Mon Sep 17 00:00:00 2001 From: Juan Castro Date: Mon, 28 Sep 2026 17:40:50 -0400 Subject: [PATCH] docs: add the medium fs recipe readdir paging, streaming, sorting and recursion, plus the write and mkdir options that decide what happens to an existing name. One playground example per section. --- src/docs/src/examples.js | 36 +++++ .../examples/fs-large-directory-page.html | 24 ++++ .../examples/fs-large-directory-parents.html | 25 ++++ .../examples/fs-large-directory-sort.html | 36 +++++ .../examples/fs-large-directory-stream.html | 19 +++ .../examples/fs-large-directory-total.html | 22 +++ .../examples/fs-large-directory-write.html | 29 ++++ .../recipes/work-with-a-large-directory.md | 131 ++++++++++++++++++ 8 files changed, 322 insertions(+) create mode 100644 src/docs/src/playground/examples/fs-large-directory-page.html create mode 100644 src/docs/src/playground/examples/fs-large-directory-parents.html create mode 100644 src/docs/src/playground/examples/fs-large-directory-sort.html create mode 100644 src/docs/src/playground/examples/fs-large-directory-stream.html create mode 100644 src/docs/src/playground/examples/fs-large-directory-total.html create mode 100644 src/docs/src/playground/examples/fs-large-directory-write.html create mode 100644 src/docs/src/recipes/work-with-a-large-directory.md diff --git a/src/docs/src/examples.js b/src/docs/src/examples.js index c9dae6d39..6880b013a 100644 --- a/src/docs/src/examples.js +++ b/src/docs/src/examples.js @@ -456,6 +456,42 @@ const examples = [ slug: 'fs-share-a-file-mode', source: '/playground/examples/fs-share-a-file-mode.html', }, +{ + title: 'page through a large directory', + description: 'Read a directory one page at a time with a cursor using Puter.js filesystem API. Run and experiment with this example in the playground.', + slug: 'fs-large-directory-page', + source: '/playground/examples/fs-large-directory-page.html', + }, +{ + title: 'stream directory pages', + description: 'Walk a large directory with an async iterator using Puter.js filesystem API. Run and modify this example instantly in your browser.', + slug: 'fs-large-directory-stream', + source: '/playground/examples/fs-large-directory-stream.html', + }, +{ + title: 'sort and descend a directory', + description: 'Sort a directory listing and read subdirectories with Puter.js filesystem API. Run and experiment with this example in the playground.', + slug: 'fs-large-directory-sort', + source: '/playground/examples/fs-large-directory-sort.html', + }, +{ + title: 'count a directory while paging', + description: 'Get a total entry count alongside a page of results with Puter.js filesystem API. Run and modify this example directly in your browser.', + slug: 'fs-large-directory-total', + source: '/playground/examples/fs-large-directory-total.html', + }, +{ + title: 'write without clobbering', + description: 'Keep both files or refuse to replace one with dedupeName and overwrite in Puter.js filesystem API. Run and experiment with this example in the playground.', + slug: 'fs-large-directory-write', + source: '/playground/examples/fs-large-directory-write.html', + }, +{ + title: 'create the folders on the way', + description: 'Write into a directory tree that does not exist yet with Puter.js filesystem API. Run and modify this example instantly in your browser.', + slug: 'fs-large-directory-parents', + source: '/playground/examples/fs-large-directory-parents.html', + }, ], }, { diff --git a/src/docs/src/playground/examples/fs-large-directory-page.html b/src/docs/src/playground/examples/fs-large-directory-page.html new file mode 100644 index 000000000..adbd15266 --- /dev/null +++ b/src/docs/src/playground/examples/fs-large-directory-page.html @@ -0,0 +1,24 @@ + + + + + + diff --git a/src/docs/src/playground/examples/fs-large-directory-parents.html b/src/docs/src/playground/examples/fs-large-directory-parents.html new file mode 100644 index 000000000..4e23abc2a --- /dev/null +++ b/src/docs/src/playground/examples/fs-large-directory-parents.html @@ -0,0 +1,25 @@ + + + + + + diff --git a/src/docs/src/playground/examples/fs-large-directory-sort.html b/src/docs/src/playground/examples/fs-large-directory-sort.html new file mode 100644 index 000000000..3737e3dac --- /dev/null +++ b/src/docs/src/playground/examples/fs-large-directory-sort.html @@ -0,0 +1,36 @@ + + + + + + + + + diff --git a/src/docs/src/playground/examples/fs-large-directory-stream.html b/src/docs/src/playground/examples/fs-large-directory-stream.html new file mode 100644 index 000000000..87d1a7ac0 --- /dev/null +++ b/src/docs/src/playground/examples/fs-large-directory-stream.html @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/docs/src/playground/examples/fs-large-directory-total.html b/src/docs/src/playground/examples/fs-large-directory-total.html new file mode 100644 index 000000000..eb03264ec --- /dev/null +++ b/src/docs/src/playground/examples/fs-large-directory-total.html @@ -0,0 +1,22 @@ + + + + + + diff --git a/src/docs/src/playground/examples/fs-large-directory-write.html b/src/docs/src/playground/examples/fs-large-directory-write.html new file mode 100644 index 000000000..94a67a5ce --- /dev/null +++ b/src/docs/src/playground/examples/fs-large-directory-write.html @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/src/docs/src/recipes/work-with-a-large-directory.md b/src/docs/src/recipes/work-with-a-large-directory.md new file mode 100644 index 000000000..69dd35d6b --- /dev/null +++ b/src/docs/src/recipes/work-with-a-large-directory.md @@ -0,0 +1,131 @@ +--- +title: Work With a Large Directory +description: "Page through a directory that is too big to read at once, sort and descend into it, and write into it without clobbering what is already there." +tags: [fs, performance] +order: 46 +--- + +**Use this when** a directory has more in it than you want in memory at once, or +when you are writing into a folder that other things are also writing into. + +Reading a whole directory is one call and covered in +[storing files](/recipes/store-files/). This is what to do when that is no +longer enough. + +## Page through it + +`readdir()` normally resolves to a plain array. Passing `cursor` — even `null` +for the first page — switches it to a page object instead: + +```js +let page = await puter.fs.readdir({ path: 'uploads', cursor: null, limit: 100 }); + +for (;;) { + for (const item of page.items) { + console.log(item.name); + } + if (!page.cursor) break; + page = await puter.fs.readdir({ path: 'uploads', cursor: page.cursor, limit: 100 }); +} +``` + +Stop when `cursor` is absent. It is present only while more pages remain, so +that is the condition — not comparing `items.length` to `limit`. + +## Stream the pages instead + +`stream: true` hands back an async iterator, which is usually what you want when +you are just walking the whole thing: + +```js +for await (const page of puter.fs.readdir({ path: 'uploads', stream: true })) { + for (const item of page.items) { + console.log(item.name); + } +} +``` + +Same pages, less bookkeeping. Combine it with `limit` to set the page size. + +## Sort it, and descend + +```js +const items = await puter.fs.readdir({ + path: 'uploads', + sortBy: 'modified', + sortOrder: 'desc', +}); +``` + +`sortBy` takes `name` (the default), `modified`, `type` or `size`. + +`recursive: true` lists what is in the subdirectories too, and `depth` bounds +how far down it goes: + +```js +const items = await puter.fs.readdir({ + path: 'uploads', + recursive: true, + depth: 2, +}); +``` + +Sorting interacts with recursion in a way worth knowing: sorting by `name` +orders by *full path*, so each directory's contents stay together. The other +fields sort across the whole subtree, so files from different folders interleave. + +## Count it + +```js +const { items, total } = await puter.fs.readdir({ + path: 'uploads', + cursor: null, + includeTotal: true, +}); +``` + +`includeTotal` also switches the result to a page object, so you get a count +without giving up paging. + +## Write into it without clobbering + +`write()` overwrites by default. Two options change that: + +```js +// Keep both: writes report-1.txt if report.txt is taken. +await puter.fs.write('uploads/report.txt', data, { dedupeName: true }); + +// Refuse instead of replacing. +await puter.fs.write('uploads/report.txt', data, { overwrite: false }); +``` + +`dedupeName` is what you want when two users might upload the same filename and +you would rather keep both than lose one. + +## Create the folders on the way + +Writing into a folder that does not exist fails unless you say otherwise: + +```js +await puter.fs.write('reports/2026/q3/summary.txt', data, { + createMissingParents: true, +}); +``` + +`mkdir()` takes the same three options, so a directory tree can be created in +one call rather than level by level. + +## Notes + +- `offset` still works but gets slower the further in you go, because the server + counts past everything you skipped. Prefer `cursor`. +- **A cursor pins the sort.** Later pages must not ask for a different `sortBy` + or `sortOrder`, and `stream` cannot be combined with `offset`. +- Each item carries `is_shared`. Only shares on the item itself count — the + children of a folder you shared report `false`, because the share lives on the + folder. See [sharing a file](/recipes/share-a-file/). +- `mkdir()` defaults `overwrite` to `false`, while `write()` defaults it to + `true`. They are not the same, which is easy to assume. +- The same three result shapes — array, page object, async iterator — are how + the other listing methods work too, including + [`puter.fs.listShared()`](/FS/listShared/).