@zenfs/core
Advanced tools
Comparing version 0.5.2 to 0.5.3
@@ -7,27 +7,22 @@ import type { Cred } from '../cred.js'; | ||
/** | ||
* Represents an *asynchronous* key-value store. | ||
* Represents an asynchronous key-value store. | ||
*/ | ||
export interface AsyncStore { | ||
/** | ||
* The name of the key-value store. | ||
* The name of the store. | ||
*/ | ||
name: string; | ||
/** | ||
* Empties the key-value store completely. | ||
* Empties the store completely. | ||
*/ | ||
clear(): Promise<void>; | ||
/** | ||
* Begins a read-write transaction. | ||
* Begins a transaction. | ||
*/ | ||
beginTransaction(type: 'readwrite'): AsyncRWTransaction; | ||
/** | ||
* Begins a read-only transaction. | ||
*/ | ||
beginTransaction(type: 'readonly'): AsyncROTransaction; | ||
beginTransaction(type: string): AsyncROTransaction; | ||
beginTransaction(): AsyncTransaction; | ||
} | ||
/** | ||
* Represents an asynchronous read-only transaction. | ||
* Represents an asynchronous transaction. | ||
*/ | ||
export interface AsyncROTransaction { | ||
export interface AsyncTransaction { | ||
/** | ||
@@ -38,7 +33,2 @@ * Retrieves the data at the given key. | ||
get(key: Ino): Promise<Uint8Array>; | ||
} | ||
/** | ||
* Represents an asynchronous read-write transaction. | ||
*/ | ||
export interface AsyncRWTransaction extends AsyncROTransaction { | ||
/** | ||
@@ -45,0 +35,0 @@ * Adds the data to the store under the given key. Overwrites any existing |
@@ -126,3 +126,3 @@ import { dirname, basename, join, resolve } from '../emulation/path.js'; | ||
try { | ||
const tx = this.store.beginTransaction('readwrite'), oldParent = dirname(oldPath), oldName = basename(oldPath), newParent = dirname(newPath), newName = basename(newPath), | ||
const tx = this.store.beginTransaction(), oldParent = dirname(oldPath), oldName = basename(oldPath), newParent = dirname(newPath), newName = basename(newPath), | ||
// Remove oldPath from parent's directory listing. | ||
@@ -194,3 +194,3 @@ oldDirNode = await this.findINode(tx, oldParent), oldDirList = await this.getDirListing(tx, oldDirNode, oldParent); | ||
async stat(p, cred) { | ||
const tx = this.store.beginTransaction('readonly'); | ||
const tx = this.store.beginTransaction(); | ||
const inode = await this.findINode(tx, p); | ||
@@ -207,3 +207,3 @@ if (!inode) { | ||
async createFile(p, flag, mode, cred) { | ||
const tx = this.store.beginTransaction('readwrite'), data = new Uint8Array(0), newFile = await this.commitNewFile(tx, p, FileType.FILE, mode, cred, data); | ||
const tx = this.store.beginTransaction(), data = new Uint8Array(0), newFile = await this.commitNewFile(tx, p, FileType.FILE, mode, cred, data); | ||
// Open the file. | ||
@@ -213,3 +213,3 @@ return new AsyncFile(this, p, flag, newFile.toStats(), data); | ||
async openFile(p, flag, cred) { | ||
const tx = this.store.beginTransaction('readonly'), node = await this.findINode(tx, p), data = await tx.get(node.ino); | ||
const tx = this.store.beginTransaction(), node = await this.findINode(tx, p), data = await tx.get(node.ino); | ||
if (!node.toStats().hasAccess(flagToMode(flag), cred)) { | ||
@@ -235,7 +235,7 @@ throw ApiError.EACCES(p); | ||
async mkdir(p, mode, cred) { | ||
const tx = this.store.beginTransaction('readwrite'), data = encode('{}'); | ||
const tx = this.store.beginTransaction(), data = encode('{}'); | ||
await this.commitNewFile(tx, p, FileType.DIRECTORY, mode, cred, data); | ||
} | ||
async readdir(p, cred) { | ||
const tx = this.store.beginTransaction('readonly'); | ||
const tx = this.store.beginTransaction(); | ||
const node = await this.findINode(tx, p); | ||
@@ -252,3 +252,3 @@ if (!node.toStats().hasAccess(R_OK, cred)) { | ||
async sync(p, data, stats) { | ||
const tx = this.store.beginTransaction('readwrite'), | ||
const tx = this.store.beginTransaction(), | ||
// We use the _findInode helper because we actually need the INode id. | ||
@@ -271,3 +271,3 @@ fileInodeId = await this._findINode(tx, dirname(p), basename(p)), fileInode = await this.getINode(tx, fileInodeId, p), inodeChanged = fileInode.update(stats); | ||
async link(existing, newpath, cred) { | ||
const tx = this.store.beginTransaction('readwrite'), existingDir = dirname(existing), existingDirNode = await this.findINode(tx, existingDir); | ||
const tx = this.store.beginTransaction(), existingDir = dirname(existing), existingDirNode = await this.findINode(tx, existingDir); | ||
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) { | ||
@@ -301,3 +301,3 @@ throw ApiError.EACCES(existingDir); | ||
async makeRootDirectory() { | ||
const tx = this.store.beginTransaction('readwrite'); | ||
const tx = this.store.beginTransaction(); | ||
if ((await tx.get(rootIno)) === undefined) { | ||
@@ -501,3 +501,3 @@ // Create new inode. o777, owned by root:root | ||
} | ||
const tx = this.store.beginTransaction('readwrite'), parent = dirname(p), parentNode = await this.findINode(tx, parent), parentListing = await this.getDirListing(tx, parentNode, parent), fileName = basename(p); | ||
const tx = this.store.beginTransaction(), parent = dirname(p), parentNode = await this.findINode(tx, parent), parentListing = await this.getDirListing(tx, parentNode, parent), fileName = basename(p); | ||
if (!parentListing[fileName]) { | ||
@@ -504,0 +504,0 @@ throw ApiError.ENOENT(p); |
import type { Ino } from '../inode.js'; | ||
import type { Backend } from './backend.js'; | ||
import { SyncStore, SimpleSyncStore, SyncRWTransaction } from './SyncStore.js'; | ||
import { SyncStore, SimpleSyncStore, SyncTransaction } from './SyncStore.js'; | ||
/** | ||
@@ -12,3 +12,3 @@ * A simple in-memory store | ||
clear(): void; | ||
beginTransaction(): SyncRWTransaction; | ||
beginTransaction(): SyncTransaction; | ||
get(key: Ino): Uint8Array; | ||
@@ -15,0 +15,0 @@ put(key: Ino, data: Uint8Array, overwrite: boolean): boolean; |
@@ -1,2 +0,2 @@ | ||
import { SimpleSyncRWTransaction, SyncStoreFS } from './SyncStore.js'; | ||
import { SimpleSyncTransaction, SyncStoreFS } from './SyncStore.js'; | ||
/** | ||
@@ -14,3 +14,3 @@ * A simple in-memory store | ||
beginTransaction() { | ||
return new SimpleSyncRWTransaction(this); | ||
return new SimpleSyncTransaction(this); | ||
} | ||
@@ -17,0 +17,0 @@ get(key) { |
@@ -19,15 +19,10 @@ import { Cred } from '../cred.js'; | ||
/** | ||
* Begins a new read-only transaction. | ||
* Begins a new transaction. | ||
*/ | ||
beginTransaction(type: 'readonly'): SyncROTransaction; | ||
/** | ||
* Begins a new read-write transaction. | ||
*/ | ||
beginTransaction(type: 'readwrite'): SyncRWTransaction; | ||
beginTransaction(type: string): SyncROTransaction; | ||
beginTransaction(): SyncTransaction; | ||
} | ||
/** | ||
* A read-only transaction for a synchronous key value store. | ||
* A transaction for a synchronous key value store. | ||
*/ | ||
export interface SyncROTransaction { | ||
export interface SyncTransaction { | ||
/** | ||
@@ -40,7 +35,2 @@ * Retrieves the data at the given key. Throws an ApiError if an error occurs | ||
get(ino: Ino): Uint8Array | undefined; | ||
} | ||
/** | ||
* A read-write transaction for a synchronous key value store. | ||
*/ | ||
export interface SyncRWTransaction extends SyncROTransaction { | ||
/** | ||
@@ -81,3 +71,3 @@ * Adds the data to the store under the given key. | ||
*/ | ||
export declare class SimpleSyncRWTransaction implements SyncRWTransaction { | ||
export declare class SimpleSyncTransaction implements SyncTransaction { | ||
protected store: SimpleSyncStore; | ||
@@ -192,3 +182,3 @@ /** | ||
*/ | ||
protected _findINode(tx: SyncROTransaction, parent: string, filename: string, visited?: Set<string>): Ino; | ||
protected _findINode(tx: SyncTransaction, parent: string, filename: string, visited?: Set<string>): Ino; | ||
/** | ||
@@ -200,3 +190,3 @@ * Finds the Inode of the given path. | ||
*/ | ||
protected findINode(tx: SyncROTransaction, p: string): Inode; | ||
protected findINode(tx: SyncTransaction, p: string): Inode; | ||
/** | ||
@@ -208,7 +198,7 @@ * Given the ID of a node, retrieves the corresponding Inode. | ||
*/ | ||
protected getINode(tx: SyncROTransaction, id: Ino, p?: string): Inode; | ||
protected getINode(tx: SyncTransaction, id: Ino, p?: string): Inode; | ||
/** | ||
* Given the Inode of a directory, retrieves the corresponding directory listing. | ||
*/ | ||
protected getDirListing(tx: SyncROTransaction, inode: Inode, p?: string): { | ||
protected getDirListing(tx: SyncTransaction, inode: Inode, p?: string): { | ||
[fileName: string]: Ino; | ||
@@ -221,3 +211,3 @@ }; | ||
*/ | ||
protected addNewNode(tx: SyncRWTransaction, data: Uint8Array): Ino; | ||
protected addNewNode(tx: SyncTransaction, data: Uint8Array): Ino; | ||
/** | ||
@@ -224,0 +214,0 @@ * Commits a new file (well, a FILE or a DIRECTORY) to the file system with the given mode. |
@@ -13,3 +13,3 @@ import { dirname, basename, join, resolve, sep } from '../emulation/path.js'; | ||
*/ | ||
export class SimpleSyncRWTransaction { | ||
export class SimpleSyncTransaction { | ||
constructor(store) { | ||
@@ -134,3 +134,3 @@ this.store = store; | ||
renameSync(oldPath, newPath, cred) { | ||
const tx = this.store.beginTransaction('readwrite'), oldParent = dirname(oldPath), oldName = basename(oldPath), newParent = dirname(newPath), newName = basename(newPath), | ||
const tx = this.store.beginTransaction(), oldParent = dirname(oldPath), oldName = basename(oldPath), newParent = dirname(newPath), newName = basename(newPath), | ||
// Remove oldPath from parent's directory listing. | ||
@@ -197,3 +197,3 @@ oldDirNode = this.findINode(tx, oldParent), oldDirList = this.getDirListing(tx, oldDirNode, oldParent); | ||
// Get the inode to the item, convert it into a Stats object. | ||
const stats = this.findINode(this.store.beginTransaction('readonly'), p).toStats(); | ||
const stats = this.findINode(this.store.beginTransaction(), p).toStats(); | ||
if (!stats.hasAccess(R_OK, cred)) { | ||
@@ -209,3 +209,3 @@ throw ApiError.EACCES(p); | ||
openFileSync(p, flag, cred) { | ||
const tx = this.store.beginTransaction('readonly'), node = this.findINode(tx, p), data = tx.get(node.ino); | ||
const tx = this.store.beginTransaction(), node = this.findINode(tx, p), data = tx.get(node.ino); | ||
if (!node.toStats().hasAccess(flagToMode(flag), cred)) { | ||
@@ -235,3 +235,3 @@ throw ApiError.EACCES(p); | ||
readdirSync(p, cred) { | ||
const tx = this.store.beginTransaction('readonly'); | ||
const tx = this.store.beginTransaction(); | ||
const node = this.findINode(tx, p); | ||
@@ -246,3 +246,3 @@ if (!node.toStats().hasAccess(R_OK, cred)) { | ||
// update is required. | ||
const tx = this.store.beginTransaction('readwrite'), | ||
const tx = this.store.beginTransaction(), | ||
// We use the _findInode helper because we actually need the INode id. | ||
@@ -265,3 +265,3 @@ fileInodeId = this._findINode(tx, dirname(p), basename(p)), fileInode = this.getINode(tx, fileInodeId, p), inodeChanged = fileInode.update(stats); | ||
linkSync(existing, newpath, cred) { | ||
const tx = this.store.beginTransaction('readwrite'), existingDir = dirname(existing), existingDirNode = this.findINode(tx, existingDir); | ||
const tx = this.store.beginTransaction(), existingDir = dirname(existing), existingDirNode = this.findINode(tx, existingDir); | ||
if (!existingDirNode.toStats().hasAccess(R_OK, cred)) { | ||
@@ -295,3 +295,3 @@ throw ApiError.EACCES(existingDir); | ||
makeRootDirectory() { | ||
const tx = this.store.beginTransaction('readwrite'); | ||
const tx = this.store.beginTransaction(); | ||
if (tx.get(rootIno)) { | ||
@@ -407,3 +407,3 @@ return; | ||
commitNewFile(p, type, mode, cred, data = new Uint8Array()) { | ||
const tx = this.store.beginTransaction('readwrite'), parentDir = dirname(p), fname = basename(p), parentNode = this.findINode(tx, parentDir), dirListing = this.getDirListing(tx, parentNode, parentDir); | ||
const tx = this.store.beginTransaction(), parentDir = dirname(p), fname = basename(p), parentNode = this.findINode(tx, parentDir), dirListing = this.getDirListing(tx, parentNode, parentDir); | ||
//Check that the creater has correct access | ||
@@ -450,3 +450,3 @@ if (!parentNode.toStats().hasAccess(W_OK, cred)) { | ||
removeEntry(p, isDir, cred) { | ||
const tx = this.store.beginTransaction('readwrite'), parent = dirname(p), parentNode = this.findINode(tx, parent), parentListing = this.getDirListing(tx, parentNode, parent), fileName = basename(p), fileIno = parentListing[fileName]; | ||
const tx = this.store.beginTransaction(), parent = dirname(p), parentNode = this.findINode(tx, parent), parentListing = this.getDirListing(tx, parentNode, parent), fileName = basename(p), fileIno = parentListing[fileName]; | ||
if (!fileIno) { | ||
@@ -453,0 +453,0 @@ throw ApiError.ENOENT(p); |
@@ -52,3 +52,3 @@ /// <reference types="node" resolution-mode="require"/> | ||
* @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string. | ||
* @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* @param _options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* If `encoding` is not supplied, the default of `'utf8'` is used. | ||
@@ -59,3 +59,3 @@ * If `mode` is not supplied, the default of `0o666` is used. | ||
*/ | ||
appendFile(data: string | Uint8Array, options?: { | ||
appendFile(data: string | Uint8Array, _options?: { | ||
encoding?: BufferEncoding; | ||
@@ -80,9 +80,9 @@ mode?: Node.Mode; | ||
* The `FileHandle` must have been opened for reading. | ||
* @param options An object that may contain an optional flag. | ||
* @param _options An object that may contain an optional flag. | ||
* If a flag is not provided, it defaults to `'r'`. | ||
*/ | ||
readFile(options?: { | ||
readFile(_options?: { | ||
flag?: Node.OpenMode; | ||
}): Promise<Uint8Array>; | ||
readFile(options: { | ||
readFile(_options: { | ||
encoding: BufferEncoding; | ||
@@ -132,3 +132,3 @@ flag?: Node.OpenMode; | ||
* @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string. | ||
* @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* @param _options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* If `encoding` is not supplied, the default of `'utf8'` is used. | ||
@@ -139,3 +139,3 @@ * If `mode` is not supplied, the default of `0o666` is used. | ||
*/ | ||
writeFile(data: string | Uint8Array, options?: Node.WriteFileOptions): Promise<void>; | ||
writeFile(data: string | Uint8Array, _options?: Node.WriteFileOptions): Promise<void>; | ||
/** | ||
@@ -142,0 +142,0 @@ * See `fs.writev` promisified version. |
@@ -68,3 +68,3 @@ import { ApiError, ErrorCode } from '../ApiError.js'; | ||
* @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string. | ||
* @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* @param _options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* If `encoding` is not supplied, the default of `'utf8'` is used. | ||
@@ -75,4 +75,13 @@ * If `mode` is not supplied, the default of `0o666` is used. | ||
*/ | ||
appendFile(data, options) { | ||
return appendFile(fd2file(this.fd).path, data, options); | ||
async appendFile(data, _options) { | ||
const options = normalizeOptions(_options, 'utf8', 'a', 0o644); | ||
const flag = parseFlag(options.flag); | ||
if (!isAppendable(flag)) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Flag passed to appendFile must allow for appending.'); | ||
} | ||
if (typeof data != 'string' && !options.encoding) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified'); | ||
} | ||
const encodedData = typeof data == 'string' ? encode(data) : data; | ||
await fd2file(this.fd).write(encodedData, 0, encodedData.length, null); | ||
} | ||
@@ -93,7 +102,16 @@ /** | ||
} | ||
readFile(options) { | ||
return readFile(fd2file(this.fd).path, options); | ||
async readFile(_options) { | ||
const options = normalizeOptions(_options, null, 'r', null); | ||
const flag = parseFlag(options.flag); | ||
if (!isReadable(flag)) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for reading.'); | ||
} | ||
const { size } = await this.stat(); | ||
const data = new Uint8Array(size); | ||
await fd2file(this.fd).read(data, 0, size, 0); | ||
return options.encoding ? decode(data, options.encoding) : data; | ||
} | ||
stat(opts) { | ||
return stat(fd2file(this.fd).path, opts); | ||
async stat(opts) { | ||
const stats = await fd2file(this.fd).stat(); | ||
return opts?.bigint ? new BigIntStats(stats) : stats; | ||
} | ||
@@ -126,3 +144,3 @@ async write(data, posOrOff, lenOrEnc, position) { | ||
* @param data The data to write. If something other than a `Buffer` or `Uint8Array` is provided, the value is coerced to a string. | ||
* @param options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* @param _options Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag. | ||
* If `encoding` is not supplied, the default of `'utf8'` is used. | ||
@@ -133,4 +151,13 @@ * If `mode` is not supplied, the default of `0o666` is used. | ||
*/ | ||
writeFile(data, options) { | ||
return writeFile(fd2file(this.fd).path, data, options); | ||
async writeFile(data, _options) { | ||
const options = normalizeOptions(_options, 'utf8', 'w', 0o644); | ||
const flag = parseFlag(options.flag); | ||
if (!isWriteable(flag)) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for writing.'); | ||
} | ||
if (typeof data != 'string' && !options.encoding) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified'); | ||
} | ||
const encodedData = typeof data == 'string' ? encode(data, options.encoding) : data; | ||
await fd2file(this.fd).write(encodedData, 0, encodedData.length, 0); | ||
} | ||
@@ -331,2 +358,3 @@ /** | ||
await file.close(); | ||
throw e; | ||
} | ||
@@ -345,17 +373,2 @@ } | ||
/** | ||
* Asynchronously writes data to a file, replacing the file | ||
* if it already exists. | ||
* | ||
* The encoding option is ignored if data is a buffer. | ||
*/ | ||
async function _writeFile(fname, data, flag, mode, resolveSymlinks) { | ||
const file = await _open(fname, flag, mode, resolveSymlinks); | ||
try { | ||
await file.write(data, 0, data.length, 0); | ||
} | ||
finally { | ||
await file.close(); | ||
} | ||
} | ||
/** | ||
* Synchronously writes data to a file, replacing the file if it already exists. | ||
@@ -373,11 +386,9 @@ * | ||
const options = normalizeOptions(_options, 'utf8', 'w', 0o644); | ||
const flag = parseFlag(options.flag); | ||
if (!isWriteable(flag)) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for writing.'); | ||
const handle = await open(filename, options.flag, options.mode); | ||
try { | ||
await handle.writeFile(data, options); | ||
} | ||
if (typeof data != 'string' && !options.encoding) { | ||
throw new ApiError(ErrorCode.EINVAL, 'Encoding not specified'); | ||
finally { | ||
await handle.close(); | ||
} | ||
const encodedData = typeof data == 'string' ? encode(data, options.encoding) : data; | ||
await _writeFile(filename, encodedData, options.flag, options.mode, true); | ||
} | ||
@@ -384,0 +395,0 @@ writeFile; |
{ | ||
"name": "@zenfs/core", | ||
"version": "0.5.2", | ||
"version": "0.5.3", | ||
"description": "A filesystem in your browser", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1472739
11348