Skip to content

Latest commit

 

History

History
616 lines (459 loc) · 21.7 KB

README.md

File metadata and controls

616 lines (459 loc) · 21.7 KB

indexeddb-fs

An fs library for the browser that lets you store data using an API similar to Node's fs module. It's powered by IndexedDB, a browser database.

npm types minzip downloads per month issues license

Motivation

Other solutions I found didn't work well. They lacked validation and didn't create directories properly.

Installation

npm install indexeddb-fs

A simple example of how you can use indexeddb-fs

import fs from 'indexeddb-fs';

async function main() {
  // Check if a directory exists
  const directoryExists = await fs.isDirectory('my_directory');

  // Create a new directory if it doesn't exist
  if (!directoryExists) {
    await fs.createDirectory('my_directory');
  }

  // Write data to a file
  const content = 'Hello, world!';
  await fs.writeFile('my_directory/my_file.txt', content);

  // Read data from the file
  const readContent = await fs.readFile('my_directory/my_file.txt');
  console.log(readContent); // "Hello, world!"

  // Remove the directory and all files within it
  await fs.removeDirectory('my_directory');
}

main();

A more complex example that demonstrates the use of all the functions

import fs from 'indexeddb-fs';

async function main() {
  // Create a new directory and subdirectories
  await fs.createDirectory('my_directory');
  await fs.createDirectory('my_directory/subdirectory');
  await fs.createDirectory('my_directory/another_subdirectory');

  // Write some content to a file
  const content = 'Hello, world!';
  await fs.writeFile('my_directory/my_file.txt', content);

  // Check if a file exists and if it's a file
  const fileExists = await fs.exists('my_directory/my_file.txt');
  const isAFile = await fs.isFile('my_directory/my_file.txt');

  console.log('File exists:', fileExists); // true
  console.log('Is a file:', isAFile); // true

  // Copy the file to a new location
  await fs.copyFile('my_directory/my_file.txt', 'my_directory/another_subdirectory/my_file_copy.txt');

  // Rename and move the original file to a new location
  await fs.renameFile('my_directory/my_file.txt', 'my_directory/new_file_name.txt');
  await fs.moveFile('my_directory/new_file_name.txt', 'my_directory/subdirectory/new_location.txt');

  // Read the contents of a directory and count the number of files and subdirectories
  const { filesCount, directoriesCount } = await fs.readDirectory('my_directory');

  console.log('Number of files:', filesCount); // 0
  console.log('Number of subdirectories:', directoriesCount); // 2

  // Remove the directory and all files within it
  await fs.removeDirectory('my_directory');

  // Read the contents of the copied file
  const copiedFileContent = await fs.readFile('my_directory/another_subdirectory/my_file_copy.txt');

  console.log('Copied file content:', copiedFileContent); // "Hello, world!"
}

main();

Custom fs object

import { createFs } from 'indexeddb-fs';

const fs = createFs({
  databaseVersion: 'indexeddb version (default "1")',
  objectStoreName: 'store name in indexeddb (default "files")',
  rootDirectoryName: 'your root directory name (default "root") ',
  databaseName: 'indexeddb database name (default "indexeddb-fs")',
});

Api

  • All methods in indexeddb-fs return a Promise since IndexedDB is an asynchronous API.
  • Each method can reject the returned Promise with an Error object when an error occurs.
  • It's important to handle errors appropriately in your code to ensure your application is robust and doesn't break unexpectedly.

Fields

The indexeddb-fs library contains several configuration fields that can be accessed after importing the library. These fields include:

  • databaseName: the name of the IndexedDB database used by the library.
  • databaseVersion: the version number of the IndexedDB database used by the library.
  • objectStoreName: the name of the object store used by the library to store data.
  • rootDirectoryName: the name of the root directory used by the library.

To access these fields, import them from the indexeddb-fs module as shown in the example code above. These fields contain configuration information for the library and can also be used to access default values.

Common functions

fs.exists(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<boolean>
  • Description: Returns a promise that resolves to true if the file or directory at the given fullPath exists, and false otherwise.

Example of usage:

// Check if a file exists
const fileExists = await fs.exists('path/to/file.txt');
if (fileExists) {
  console.log('The file exists!');
} else {
  console.log('The file does not exist.');
}

// Check if a directory exists
const dirExists = await fs.exists('path/to/directory');
if (dirExists) {
  console.log('The directory exists!');
} else {
  console.log('The directory does not exist.');
}

fs.remove(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<void>
  • Description: Removes the file or directory at the given fullPath. The method does not remove directories recursively, so it will throw an error if the path is not empty.

Example of usage:

// Remove a file
await fs.writeFile('file1.txt', 'test content');
await fs.remove('file1.txt');
await fs.exists('file1.txt').then((result) => {
  console.log(!result);
});

// Remove an empty directory
await fs.createDirectory('directory1');
await fs.remove('directory1');
await fs.exists('directory1').then((result) => {
  console.log(!result);
});

// Attempt to remove a non-empty directory
await fs.createDirectory('directory2');
await fs.writeFile('directory2/file2.txt', 'test content');
await fs.remove('directory2').catch((error) => {
  console.error(error.message);
});
await fs.exists('directory2').then((result) => {
  console.log(result);
});

fs.details(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<FileEntry<any> | DirectoryEntry>
  • Description: Returns an object containing details about the file or directory at the given fullPath. The object contains the following properties:
    • type: The type of the entry (file or directory).
    • name: The name of the entry.
    • directory: The name of the directory that contains the entry.
    • fullPath: The full path of the entry, including the directory.
  • Throws an error when the path does not contain anything.

Example of usage:

// Get details of a newly created directory
const createdDirectory = await fs.createDirectory('directory');
const createdDirectoryDetails = await fs.details(createdDirectory.fullPath);

console.log('Type:', createdDirectoryDetails.type); // directory
console.log('Name:', createdDirectoryDetails.name); // directory
console.log('Directory:', createdDirectoryDetails.directory); // root
console.log('Full Path:', createdDirectoryDetails.fullPath); // root/directory

Example result for FileEntry<any> type:

{
    type: 'file',
    name: 'file.txt',
    directory: 'root',
    data: 'test 2 content',
    createdAt: 1626882161631,
    fullPath: 'root/file.txt'
}

Example result for DirectoryEntry type:

{
    isRoot: false,
    directory: 'root',
    type: 'directory',
    name: 'directory',
    createdAt: 1626882291087,
    fullPath: 'root/directory'
}

File functions

fs.isFile(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<boolean>
  • Description: Returns a promise that resolves to true if a file exists at the given fullPath, and false otherwise. If the path does not contain anything, an error is thrown.

Example of usage:

import fs from 'indexeddb-fs';

// Create directories for testing
await fs.createDirectory('files');
await fs.createDirectory('directories');

// Check if directories are files
await fs.isFile('files').then((result) => {
  console.log(result);
});
await fs.isFile('directories').then((result) => {
  console.log(result);
});

// Create a file and check if it is a file
await fs.writeFile('file', 'content');
await fs.isFile('file').then((result) => {
  console.log(result);
});

// Create a file in the 'files' directory and check if it is a file
await fs.writeFile('files/file', 'content');
await fs.isFile('files/file').then((result) => {
  console.log(result);
});

Example result for FileEntry<TData> type:

{
    type: 'file',
    name: 'file.txt',
    directory: 'root',
    data: 'test 2 content',
    createdAt: 1626882161631,
    fullPath: 'root/file.txt'
}

fs.writeFile(fullPath, data)

  • Parameters: [fullPath: string, data: TData]
  • Returns: Promise<FileEntry<TData>>
  • Description: Writes data to the file specified by fullPath, replacing the file if it already exists. The data parameter can be any data you want to write to the file, and is usually a string or an object that can be serialized to JSON. The method returns a promise that resolves to a FileEntry object representing the file that was written.
  • Throws an error when the destination directory of the file does not exist. This can happen if the path to the file contains one or more directories that do not exist yet. You can use the createDirectory method to create the missing directories.
  • Throws an error when the path contains a directory with the same name as the file. For example, if a directory named "file.txt" exists, you cannot create a file with the same name in the same directory.

Example of usage:

import fs from 'indexeddb-fs';

// Create a directory to write the file to
await fs.createDirectory('my_directory');

// Write some data to a file
const fileEntry = await fs.writeFile('my_directory/my_file.txt', 'Hello, world!');

console.log(fileEntry); // FileEntry object representing the file that was written

Example result for FileEntry<TData> type:

{
    type: 'file',
    name: 'file.txt',
    directory: 'root',
    data: 'test 2 content',
    createdAt: 1626882161631,
    fullPath: 'root/file.txt'
}

fs.fileDetails(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<FileEntry<TData>>
  • Description: Returns a promise that resolves to a FileEntry object containing details about the file specified by fullPath. The FileEntry object includes information such as the file's name, size, modification date, and type. If the file does not exist, or if the path contains directories that do not exist, the method will throw an error.
  • Throws an error when the path does not contain anything. This can happen if the fullPath parameter is an empty string or null.
  • Throws an error when the destination file is not a file. For example, if the path points to a directory, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Get details about a file
const fileEntry = await fs.fileDetails('my_directory/my_file.txt');

console.log(fileEntry); // FileEntry object representing the file

fs.readFile(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<TData>
  • Description: Returns a promise that resolves to the contents of the file specified by fullPath. The returned data type is the same type with which it was saved. For example, if the file was saved as a string using the writeFile method, the returned data type will also be a string. If the file was saved as an object using the writeFile method, the returned data type will also be an object.
  • Throws an error when the destination file does not exist. This can happen if the file was deleted or if the fullPath parameter points to a file that does not exist yet.
  • Throws an error when the destination file is not a file. For example, if the path points to a directory, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Read the contents of a file
const fileContents = await fs.readFile('my_directory/my_file.txt');

console.log(fileContents); // Contents of the file

fs.removeFile(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<void>
  • Description: Removes the file specified by fullPath. The method returns a promise that resolves once the file has been successfully removed.
  • Throws an error when the path does not contain anything. This can happen if the fullPath parameter is an empty string or null.
  • Throws an error when the destination file is not a file. For example, if the path points to a directory, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Remove a file
await fs.removeFile('my_directory/my_file.txt');

fs.renameFile(fullPath, newFilename)

  • Parameters: [fullPath: string, newFilename: string]
  • Returns: Promise<FileEntry<TData>>
  • Description: Renames the file specified by fullPath to the new filename provided as newFilename. The method returns a promise that resolves to a FileEntry object representing the renamed file.
  • Throws an error when the path does not contain anything. This can happen if the fullPath parameter is an empty string or null.
  • Throws an error when the fullPath is not a file. For example, if the path points to a directory, the method will throw an error.
  • Throws an error when the fullPath with a newFilename is already taken. For example, if a file named newFilename already exists in the same directory as the original file, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Rename a file
const renamedFile = await fs.renameFile('my_directory/my_file.txt', 'new_file.txt');

console.log(renamedFile); // FileEntry object representing the renamed file

Example result for FileEntry<TData> type:

{
    type: 'file',
    name: 'file.txt',
    directory: 'root',
    data: 'test 2 content',
    createdAt: 1626882161631,
    fullPath: 'root/file.txt'
}

fs.copyFile(fullPath, destinationPath)

  • Parameters: [fullPath: string, destinationPath: string]
  • Returns: Promise<FileEntry<TData>>
  • Description: Copies the file specified by fullPath to the destination path specified by destinationPath. The method returns a promise that resolves to a FileEntry object representing the copied file at the new destinationPath.
  • Throws an error when the path does not contain anything. This can happen if the fullPath or destinationPath parameters are empty strings or null.
  • Throws an error when the fullPath is not a file. For example, if the path points to a directory, the method will throw an error.
  • Throws an error when the destinationPath is already taken. For example, if a file or directory already exists at the destinationPath, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Copy a file
const copiedFile = await fs.copyFile('my_directory/my_file.txt', 'my_directory/copied_file.txt');

console.log(copiedFile); // FileEntry object representing the copied file

Example result for FileEntry<TData> type:

{
    type: 'file',
    name: 'file.txt',
    directory: 'root',
    data: 'test 2 content',
    createdAt: 1626882161631,
    fullPath: 'root/file.txt'
}

fs.moveFile(fullPath, destinationPath)

  • Parameters: [fullPath: string, destinationPath: string]
  • Returns: Promise<FileEntry<TData>>
  • Description: Moves the file specified by fullPath to the destination path specified by destinationPath. The method returns a promise that resolves to a FileEntry object representing the moved file at the new destinationPath.
  • Throws an error when the path does not contain anything. This can happen if the fullPath or destinationPath parameters are empty strings or null.
  • Throws an error when the fullPath is not a file. For example, if the path points to a directory, the method will throw an error.
  • Throws an error when the destinationPath is already taken. For example, if a file or directory already exists at the destinationPath, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Move a file
const movedFile = await fs.moveFile('my_directory/my_file.txt', 'my_directory/moved_file.txt');

console.log(movedFile); // FileEntry object representing the moved file

Example result for FileEntry<TData> type:

{
    type: 'file',
    name: 'file.txt',
    directory: 'root',
    data: 'test 2 content',
    createdAt: 1626882161631,
    fullPath: 'root/file.txt'
}

Directory functions

fs.isDirectory(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<boolean>
  • Description: Returns true if the path specified by fullPath contains a directory, false otherwise.
  • Throws an error when the path does not contain anything. This can happen if the fullPath parameter is an empty string or null.

Example of usage:

import fs from 'indexeddb-fs';

// Check if a path is a directory
const isDirectory = await fs.isDirectory('my_directory');

console.log(isDirectory); // true if 'my_directory' is a directory, false otherwise

fs.createDirectory(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<DirectoryEntry>
  • Description: Creates a new directory at the path specified by fullPath and returns a Promise that resolves to a DirectoryEntry object representing the new directory.
  • Throws an error when the destination directory of the directory does not exist. For example, if the fullPath parameter specifies a nested directory structure, the method will throw an error if any of the parent directories do not already exist.
  • Throws an error when the path contains a file with the same name. For example, if a file called 'my_directory' already exists in the specified path, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Create a new directory
const newDirectory = await fs.createDirectory('my_directory');

console.log(newDirectory); // DirectoryEntry object representing the new directory

Example result for DirectoryEntry type:

{
    isRoot: false,
    directory: 'root',
    type: 'directory',
    name: 'directory',
    createdAt: 1626882291087,
    fullPath: 'root/directory'
}

fs.readDirectory(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<ReadDirectoryInstanceOutput>
  • Description: Reads the entire contents of the directory specified by fullPath and returns a Promise that resolves to an object containing an array of DirectoryEntry and FileEntry objects representing the contents of the directory, as well as a count of the number of directories and files in the directory.
  • Throws an error when the destination directory does not exist. For example, if the fullPath parameter specifies a nested directory structure, the method will throw an error if any of the parent directories do not exist.
  • Throws an error when the destination directory is not a directory. For example, if the fullPath parameter specifies a file rather than a directory, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Read the contents of a directory
const directoryContents = await fs.readDirectory('my_directory');

console.log(directoryContents);
// { files: [...], directories: [...], filesCount: 2, directoriesCount: 1 }

Example result for ReadDirectoryInstanceOutput type:

{
    files: [],
    filesCount: 0,
    isEmpty: true,
    directories: [],
    directoriesCount: 0,
}

fs.directoryDetails(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<DirectoryEntry>
  • Description: Returns an object with details about the directory specified by fullPath. The method returns a Promise that resolves to a DirectoryEntry object representing the directory.
  • Throws an error when the path does not contain anything. For example, if the fullPath parameter is an empty string or undefined, the method will throw an error.
  • Throws an error when the destination directory is not a directory. For example, if the fullPath parameter specifies a file rather than a directory, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Get details about a directory
const directory = await fs.directoryDetails('my_directory');

console.log(directory);
// DirectoryEntry object representing the directory

Example result for DirectoryEntry type:

{
    isRoot: false,
    directory: 'root',
    type: 'directory',
    name: 'directory',
    createdAt: 1626882291087,
    fullPath: 'root/directory'
}

fs.removeDirectory(fullPath)

  • Parameters: [fullPath: string]
  • Returns: Promise<void>
  • Description: Removes the directory specified by fullPath, recursively removing any files/subdirectories contained within. The method returns a Promise that resolves once the directory has been removed.
  • Throws an error when the destination directory does not exist. For example, if the fullPath parameter specifies a nested directory structure, the method will throw an error if any of the parent directories do not exist.
  • Throws an error when the destination directory is not a directory. For example, if the fullPath parameter specifies a file rather than a directory, the method will throw an error.

Example of usage:

import fs from 'indexeddb-fs';

// Remove a directory
await fs.removeDirectory('my_directory');

License

This project is licensed under the MIT License - see the LICENSE file for details.