Maps web URLs to filesystem paths with proper encoding and normalization, supporting treating paths as both files and directories through an index file convention.
npm install url-file-dbconst { url_file_db } = require('url-file-db')
// Create a database watching a directory
const db = await url_file_db.create('./data', (canonical_path) => {
console.log('File changed:', canonical_path)
})
// Convert URL to canonical path
const path = url_file_db.url_path_to_canonical_path('/hello/world.png?foo=bar')
// -> '/hello/world.png'
// Read and write files
await db.write(path, Buffer.from('Hello, World!'))
const content = await db.read(path)
// Delete files
await db.delete(path)- URL to filesystem mapping - Converts URL paths to filesystem paths with proper encoding
- Index file convention - URLs like
/aand/a/indexmap to the same resource - Automatic file-to-directory conversion - Writing
/a/bafter/aconverts/ato a directory - Special character handling - Encodes unsafe characters using percent-encoding
- Windows reserved names - Handles reserved names like
CON,PRN,AUX, etc. - Case collision handling - Detects case-insensitive filesystems and encodes conflicts
- Path normalization - Handles
/../,//, query strings, fragments - Unicode normalization - Applies NFC normalization for consistent handling
- File watching - Monitors directory for external changes via chokidar
- Concurrency management - Promise chains ensure sequential operations per path
Creates a database instance watching the specified directory.
base_dir- Directory to store filescallback- Optional function called when files change externally (not fromdb.write)
Returns a promise that resolves to a db object with read, write, and delete methods.
Converts a URL path to a canonical path. Removes query strings, fragments, normalizes /index, and decodes percent-encoding.
url_file_db.url_path_to_canonical_path('/a/b/c') // -> '/a/b/c'
url_file_db.url_path_to_canonical_path('/a/b?query=1') // -> '/a/b'
url_file_db.url_path_to_canonical_path('/a/b#section') // -> '/a/b'
url_file_db.url_path_to_canonical_path('/a/hello%20world') // -> '/a/hello world'
url_file_db.url_path_to_canonical_path('/a/b/index') // -> '/a/b'Reads a file by its canonical path. Returns a promise that resolves to the file contents (Buffer) or null if not found.
Writes content to a file by its canonical path. Creates directories as needed.
Deletes a file by its canonical path. Returns true if deleted, false if not found.
Encodes a path component for safe filesystem storage. Handles special characters, Windows reserved names, and trailing dots/spaces.
Detects whether a directory is on a case-sensitive filesystem. Returns a promise that resolves to true (case-sensitive) or false (case-insensitive).
The special path component index is treated as equivalent to its parent:
// These URLs all normalize to the same canonical path '/a'
url_file_db.url_path_to_canonical_path('/a') // -> '/a'
url_file_db.url_path_to_canonical_path('/a/index') // -> '/a'
url_file_db.url_path_to_canonical_path('/a/index/foo') // -> '/a'
// Read and write are equivalent
await db.write(url_file_db.url_path_to_canonical_path('/a'), 'content')
await db.read(url_file_db.url_path_to_canonical_path('/a/index')) // Same resultWhen you write to a nested path under an existing file, the file is automatically converted to a directory with an index file:
// Write /a as a file
await db.write(url_file_db.url_path_to_canonical_path('/a'), 'original content')
// Filesystem: ./data/a
// Write /a/b - automatically converts /a to a directory
await db.write(url_file_db.url_path_to_canonical_path('/a/b'), 'nested content')
// Filesystem: ./data/a/index (contains "original content")
// ./data/a/b (contains "nested content")
// Reading /a still returns the original content
await db.read(url_file_db.url_path_to_canonical_path('/a')) // -> 'original content'const { url_file_db } = require('url-file-db')
const db = await url_file_db.create('./data', (canonical_path) => {
console.log('Changed:', canonical_path)
})
// Query strings are stripped
const path1 = url_file_db.url_path_to_canonical_path('/api/user?id=123')
// -> '/api/user'
// /index paths are normalized
const path2 = url_file_db.url_path_to_canonical_path('/docs/index')
// -> '/docs'
// Special characters are encoded when written to disk
const path3 = url_file_db.url_path_to_canonical_path('/test/file with spaces.txt')
await db.write(path3, 'content')
// Creates ./data/test/file%20with%20spaces.txt
// Windows reserved names are encoded
const path4 = url_file_db.url_path_to_canonical_path('/test/CON')
await db.write(path4, 'content')
// Creates ./data/test/CO%4E (not CON)
// Case conflicts are handled on case-insensitive filesystems
await db.write(url_file_db.url_path_to_canonical_path('/test/File.txt'), 'uppercase')
await db.write(url_file_db.url_path_to_canonical_path('/test/file.txt'), 'lowercase')
// On Mac/Windows: ./data/test/File.txt and ./data/test/fil%65.txt
// File-to-directory conversion
await db.write(url_file_db.url_path_to_canonical_path('/a'), 'a content')
await db.write(url_file_db.url_path_to_canonical_path('/a/b'), 'b content')
await db.write(url_file_db.url_path_to_canonical_path('/a/b/c'), 'c content')
// Filesystem: ./data/a/index (contains "a content")
// ./data/a/b/index (contains "b content")
// ./data/a/b/c (contains "c content")npm test