folder.api turns your HTTP server's directory listings into a structured JavaScript API for the browser.
Generic, server-agnostic parsing with recursion, iframe fallback, and optional parallel MIME enrichment.
npm install folder-api
Browser-only runtime (requires fetch + DOMParser). No CommonJS build is published.
import { folderApiRequest } from 'folder-api';
const res = await folderApiRequest('https://example.com/public/', {
maxDepth: 1,
includeMime: true,
headConcurrency: 8
});
console.log('files', res.files.length, 'folders', res.folders.length);{
"url": "https://example.com/public/",
"root": {
"kind": "folder",
"url": "https://example.com/public/",
"role": "self",
"depth": 0,
"children": [ { "url": "https://example.com/public/images/", "role":"child", "kind":"folder", "depth":1 } ],
"files": [ { "kind":"file", "url":"https://example.com/public/readme.txt", "name":"readme.txt", "size": 512, "date":"2024-03-01T12:00:00.000Z" } ]
},
"folders": [ /* FolderEntry[] */ ],
"files": [ /* FileEntry[] */ ],
"entries": [ /* flattened union of both */ ],
"generatedAt": "2024-03-01T12:00:05.123Z",
"errors": [],
"stats": { "fetches": 1, "iframes": 0, "heads": 5, "durationMs": 134, "maxDepth": 1 }
}- Generic heuristics (no server signature sniffing)
- Recursive traversal with depth cap
- Modes:
fetch,iframe, orauto(fetch with iframe fallback) - Parallel HEAD requests for MIME / size enrichment (configurable concurrency)
- ISO 8601 UTC date normalization
- Size parsing with unit heuristics (K, M, G) & ambiguity guards
- Hidden detection (
.dotfileexcluding./..) - Hierarchical tree + flattened arrays
- Abortable via
AbortSignal; per-directory timeout - Safety limit (50k entries) to prevent runaway traversal
- NGINX (
autoindex on) - Apache (
mod_autoindexstandard + fancy) - IIS (Directory Browsing enabled)
- Generic / other listings (best‑effort heuristics)
async function folderApiRequest(url: string, options?: FolderApiOptions): Promise<FolderApiResult>Key option defaults:
| Option | Default | Notes |
|---|---|---|
maxDepth |
0 | Depth of child folders to traverse (0 = just start directory) |
mode |
auto |
fetch |
includeMime |
false | Enables HEAD enrichment |
headConcurrency |
4 | Parallel HEAD limit (>=1) |
timeoutMs |
15000 | Per directory (fetch / iframe / HEAD) |
sameOriginOnly |
true | Caller ensures origin policy; iframe fallback relies on same-origin |
Returned FolderApiResult fields (simplified):
| Field | Description |
|---|---|
url |
Normalized starting directory URL |
root |
Root FolderNode (tree) |
folders / files |
Flat arrays of entries (v1-style names) |
entries |
Concatenated array of folders + files |
generatedAt |
ISO timestamp when assembled |
errors |
Parse / enrichment warnings (prefixed categories) |
stats |
{ fetches, iframes, heads, durationMs, maxDepth } |
fetch– Direct HTTP GET; fastest when CORS allows.iframe– Browser-only sandboxed load (allow-same-origin) used when fetch blocked.auto– Attempt fetch; on failure (network, non-200) retry via iframe.
Enable with includeMime: true. Each file may gain:
mime: fromContent-Typesize: filled / overridden fromContent-Lengthwhen missing Concurrency controlled byheadConcurrency(default 4).
Prefixes help classify issues (non-fatal):
date: size: mime: decode: loop: limit:
- Pure ESM (no CJS wrapper).
- Unified result structure (
root,folders,files,entries). - Removed previous
serverdetection field (intentionally generic now). - Added
stats.durationMs, granular counters (fetches,iframes,heads). - Added optional MIME enrichment + concurrency.
- Added iframe fallback logic in
automode.
Use the distributed ESM bundle (dist/index.js) or the browser bundle (folder-api.cdn.js, attaches window.folderApiRequest). Ensure same-origin or enable directory listing with permissive CORS for fetch mode.
const ac = new AbortController();
setTimeout(()=>ac.abort(), 5000);
await folderApiRequest('https://example.com/public/', { signal: ac.signal });npm test
npm run build
Mock server fixtures (FastAPI) in mock_servers/ provide Apache/Nginx/IIS/Caddy headers and listing layouts.
The project includes lightweight FastAPI apps that emulate Apache, Nginx, IIS, and Caddy directory listings (HTML structure + Server header). They are not literal daemons but are structurally similar outputs for parser robustness.
Ports / variants:
| Port | Name | Layout Style | Notes |
|---|---|---|---|
| 8101 | apache | <table> rows with headers (Name / Last modified / Size) |
Approximates Apache mod_autoindex |
| 8102 | nginx | <pre> lines anchor + date + size suffix |
Similar to common NGINX autoindex |
| 8103 | iis | <pre> lines with AM/PM + <dir> |
Stand-in for IIS style variations |
| 8104 | caddy | <table> rows with name / size / modified |
Approximates Caddy browse output |
Start all four locally using Astral uv (preferred, no manual venv):
uv run mock_servers/run.py
Or explicitly pin (optional):
uv run --with fastapi --with uvicorn mock_servers/run.py
Legacy manual setup (fallback):
python -m venv .venv; .\.venv\Scripts\Activate.ps1
pip install fastapi uvicorn
python mock_servers/run.py
Expected logs:
[mock_servers] starting apache on http://127.0.0.1:8101/root/
[mock_servers] starting nginx on http://127.0.0.1:8102/root/
[mock_servers] starting iis on http://127.0.0.1:8103/root/
[mock_servers] starting caddy on http://127.0.0.1:8104/root/
Example request against one mock server:
import { folderApiRequest } from 'folder-api';
const res = await folderApiRequest('http://127.0.0.1:8101/root/', { maxDepth: 1, includeMime: true });
console.log(res.stats, res.files.map(f=>f.name));Why not real daemons? Spinning up actual Apache/Nginx/IIS/Caddy for CI is heavy; HTML structural diversity is what the parser needs. If you contribute new heuristics, add another layout variant here.
Note: Built-in Python http.server isn't used because its listing format overlaps existing variants; add it if a distinct edge case emerges.
Please add / update tests for any behavioral change. Keep patches minimal; avoid adding heavy dependencies.
If you need authentic HTML directory listings (beyond the mock fixtures) you can spin up real servers quickly using Docker for the Linux variants and optional local IIS for Windows.
Prereqs: Docker Desktop (WSL2 backend on Windows) or any Docker engine.
docker compose up -d --pull always
Ports exposed:
| Server | Port | Base URL |
|---|---|---|
| Caddy | 8080 | http://localhost:8080/ |
| Nginx | 8081 | http://localhost:8081/ |
| Apache | 8082 | http://localhost:8082/ |
All three mount the repository root read-only and have directory listings forced on even if index.html exists. (Apache config enables FancyIndexing with an HTML table; Caddy & Nginx use their native formats.)
Stop and clean:
docker compose down
To add IIS output (its listing HTML differs) without disturbing existing sites, create a temporary site:
PowerShell (run as Administrator):
Import-Module WebAdministration
$path = (Resolve-Path .).Path
New-WebSite -Name folderapi-temp -Port 8083 -PhysicalPath $path -Force
Set-WebConfigurationProperty -Filter /system.webServer/directoryBrowse -PSPath IIS:\ -Name enabled -Value true -Location folderapi-temp
Restart-WebAppPool -Name 'folderapi-temp'
Remove when finished:
Remove-WebSite -Name folderapi-temp
After starting the servers (and optionally IIS), run the Python collector (Astral uv preferred):
uv run scripts/collect_listings.py
or plain Python (ensure requests):
python -m pip install requests
python scripts/collect_listings.py
Output is written under listings/<server>/<relative/path>/listing.html mirroring the repo's directory tree. Failures create listing.error.txt.
Exclude patterns: .git, node_modules, dist, build, .servers, .idea, .vscode, coverage.
If you prefer fully portable binaries instead of Docker:
- Caddy: Download
caddy_windows_amd64.zipfrom the official GitHub releases, extractcaddy.exeto a temp dir, then run:
.\caddy.exe file-server --root "C:\path\to\folder.api" --listen :8080 --browse
- Nginx: Download the Windows zip from https://nginx.org/en/download.html, edit
conf/nginx.conf:
http { server { listen 8081; root C:/path/to/folder.api; autoindex on; } }
Then run nginx.exe.
3. Apache (Apache Lounge build): Extract, edit conf/httpd.conf:
- Change
Listen 8082 - Set
DocumentRootand<Directory>to the repo path - Ensure
Options IndexesandLoadModule autoindex_module modules/mod_autoindex.soStart in foreground:httpd.exe -X -f conf/httpd.conf.
- IIS: Use the steps above (create temporary site on port 8083).
Then run the Python collector script (it will skip any server not reachable):
uv run scripts/collect_listings.py
Docker: docker compose down -v (or use native package manager in WSL: sudo apt remove caddy nginx apache2).
Portable: Stop processes / delete extracted folders.
IIS: powershell -ExecutionPolicy Bypass -File scripts/iis-temp-site.ps1 -Action remove.
Instead of Docker, you can install packages inside WSL (Ubuntu example):
sudo apt update
sudo apt install -y nginx apache2 caddy
Adjust configs to point their roots to the cloned repo path (enable autoindex / browse). Then run the collector from Windows or inside WSL (ensure the servers bind to 0.0.0.0 or localhost). Caddy: caddy file-server --root /mnt/c/Users/.../folder.api --listen :8080 --browse.
MIT (c) Paul Ellis
See llm.md for architecture & automated assistant guidance.