New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@zenfs/core

Package Overview
Dependencies
Maintainers
1
Versions
165
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zenfs/core - npm Package Compare versions

Comparing version 0.11.2 to 0.12.0

dist/backends/index/fs.d.ts

51

dist/backends/fetch.d.ts

@@ -1,5 +0,5 @@

import { NoSyncFile } from '../file.js';
import type { FileSystemMetadata } from '../filesystem.js';
import { Stats } from '../stats.js';
import { type ListingTree, type IndexFileInode, AsyncIndexFS } from './Index.js';
import { IndexFS } from './index/fs.js';
import type { IndexData } from './index/index.js';
/**

@@ -13,3 +13,3 @@ * Configuration options for FetchFS.

*/
index?: string | ListingTree;
index?: string | IndexData;
/** Used as the URL prefix for fetched files.

@@ -21,49 +21,36 @@ * Default: Fetch files relative to the index.

/**
* A simple filesystem backed by HTTP using the fetch API.
* A simple filesystem backed by HTTP using the `fetch` API.
*
*
* Listings objects look like the following:
* Index objects look like the following:
*
* ```json
* {
* "home": {
* "jvilk": {
* "someFile.txt": null,
* "someDir": {
* // Empty directory
* }
* }
* }
* "version": 1,
* "entries": {
* "/home": { ... },
* "/home/jvilk": { ... },
* "/home/james": { ... }
* }
* }
* ```
*
* This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.
* Each entry contains the stats associated with the file.
*/
export declare class FetchFS extends AsyncIndexFS<Stats> {
readonly prefixUrl: string;
protected _init: Promise<void>;
protected _initialize(index: string | ListingTree): Promise<void>;
export declare class FetchFS extends IndexFS {
readonly baseUrl: string;
ready(): Promise<void>;
constructor({ index, baseUrl }: FetchOptions);
metadata(): FileSystemMetadata;
empty(): void;
/**
* Special function: Preload the given file into the index.
* Preload the given file into the index.
* @param path
* @param buffer
*/
preloadFile(path: string, buffer: Uint8Array): void;
protected statFileInode(inode: IndexFileInode<Stats>, path: string): Promise<Stats>;
protected openFileInode(inode: IndexFileInode<Stats>, path: string, flag: string): Promise<NoSyncFile<this>>;
private _getRemotePath;
preload(path: string, buffer: Uint8Array): void;
/**
* Asynchronously download the given file.
* @todo Be lazier about actually requesting the data?
*/
protected _fetchFile(path: string, type: 'buffer'): Promise<Uint8Array>;
protected _fetchFile(path: string, type: 'json'): Promise<object>;
protected _fetchFile(path: string, type: 'buffer' | 'json'): Promise<object>;
/**
* Only requests the HEAD content, for the file size.
*/
protected _fetchSize(path: string): Promise<number>;
protected getData(path: string, stats: Stats): Promise<Uint8Array>;
protected getDataSync(path: string, stats: Stats): Uint8Array;
}

@@ -70,0 +57,0 @@ export declare const Fetch: {

@@ -1,5 +0,3 @@

import { ErrnoError, Errno } from '../error.js';
import { NoSyncFile } from '../file.js';
import { Stats } from '../stats.js';
import { FileIndex, AsyncIndexFS } from './Index.js';
import { Errno, ErrnoError } from '../error.js';
import { IndexFS } from './index/fs.js';
async function fetchFile(path, type) {

@@ -27,54 +25,35 @@ const response = await fetch(path).catch(e => {

/**
* Asynchronously retrieves the size of the given file in bytes.
* @hidden
*/
async function fetchSize(path) {
const response = await fetch(path, { method: 'HEAD' }).catch(e => {
throw new ErrnoError(Errno.EIO, e.message);
});
if (!response.ok) {
throw new ErrnoError(Errno.EIO, 'fetch failed: HEAD response returned code ' + response.status);
}
return parseInt(response.headers.get('Content-Length') || '-1', 10);
}
/**
* A simple filesystem backed by HTTP using the fetch API.
* A simple filesystem backed by HTTP using the `fetch` API.
*
*
* Listings objects look like the following:
* Index objects look like the following:
*
* ```json
* {
* "home": {
* "jvilk": {
* "someFile.txt": null,
* "someDir": {
* // Empty directory
* }
* }
* }
* "version": 1,
* "entries": {
* "/home": { ... },
* "/home/jvilk": { ... },
* "/home/james": { ... }
* }
* }
* ```
*
* This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.
* Each entry contains the stats associated with the file.
*/
export class FetchFS extends AsyncIndexFS {
async _initialize(index) {
if (typeof index != 'string') {
this._index = FileIndex.FromListing(index);
export class FetchFS extends IndexFS {
async ready() {
if (this._isInitialized) {
return;
}
try {
const response = await fetch(index);
this._index = FileIndex.FromListing((await response.json()));
await super.ready();
/**
* Iterate over all of the files and cache their contents
*/
for (const [path, stats] of this.index.files()) {
await this.getData(path, stats);
}
catch (e) {
throw new ErrnoError(Errno.EINVAL, 'Invalid or unavailable file listing tree');
}
}
async ready() {
await this._init;
}
constructor({ index = 'index.json', baseUrl = '' }) {
super({});
super(typeof index != 'string' ? index : fetchFile(index, 'json'));
// prefix url must end in a directory separator.

@@ -84,4 +63,3 @@ if (baseUrl.at(-1) != '/') {

}
this.prefixUrl = baseUrl;
this._init = this._initialize(index);
this.baseUrl = baseUrl;
}

@@ -95,60 +73,35 @@ metadata() {

}
empty() {
for (const file of this._index.files()) {
delete file.data.fileData;
}
}
/**
* Special function: Preload the given file into the index.
* Preload the given file into the index.
* @param path
* @param buffer
*/
preloadFile(path, buffer) {
const inode = this._index.get(path);
if (!inode) {
preload(path, buffer) {
const stats = this.index.get(path);
if (!stats) {
throw ErrnoError.With('ENOENT', path, 'preloadFile');
}
if (!inode.isFile()) {
if (!stats.isFile()) {
throw ErrnoError.With('EISDIR', path, 'preloadFile');
}
const stats = inode.data;
stats.size = buffer.length;
stats.fileData = buffer;
}
async statFileInode(inode, path) {
const stats = inode.data;
// At this point, a non-opened file will still have default stats from the listing.
if (stats.size < 0) {
stats.size = await this._fetchSize(path);
}
return stats;
}
async openFileInode(inode, path, flag) {
const stats = inode.data;
// Use existing file contents. This maintains the previously-used flag.
/**
* @todo Be lazier about actually requesting the data?
*/
async getData(path, stats) {
if (stats.fileData) {
return new NoSyncFile(this, path, flag, new Stats(stats), stats.fileData);
return stats.fileData;
}
// @todo be lazier about actually requesting the file
const data = await this._fetchFile(path, 'buffer');
// we don't initially have file sizes
stats.size = data.length;
const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer');
stats.fileData = data;
return new NoSyncFile(this, path, flag, new Stats(stats), data);
return data;
}
_getRemotePath(filePath) {
if (filePath.charAt(0) === '/') {
filePath = filePath.slice(1);
getDataSync(path, stats) {
if (stats.fileData) {
return stats.fileData;
}
return this.prefixUrl + filePath;
throw new ErrnoError(Errno.ENODATA, '', path, 'getData');
}
_fetchFile(path, type) {
return fetchFile(this._getRemotePath(path), type);
}
/**
* Only requests the HEAD content, for the file size.
*/
_fetchSize(path) {
return fetchSize(this._getRemotePath(path));
}
}

@@ -155,0 +108,0 @@ export const Fetch = {

@@ -360,3 +360,3 @@ /// <reference types="node" resolution-mode="require"/>

export declare class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
constructor(_fs: T, _path: string, _flag: string, _stat: Stats, contents?: Uint8Array);
constructor(fs: T, path: string, flag: string, stats: Stats, contents?: Uint8Array);
/**

@@ -363,0 +363,0 @@ * Asynchronous sync. Doesn't do anything, simply calls the cb.

@@ -477,4 +477,4 @@ import { ErrnoError, Errno } from './error.js';

export class NoSyncFile extends PreloadFile {
constructor(_fs, _path, _flag, _stat, contents) {
super(_fs, _path, _flag, _stat, contents);
constructor(fs, path, flag, stats, contents) {
super(fs, path, flag, stats, contents);
}

@@ -481,0 +481,0 @@ /**

@@ -5,3 +5,3 @@ export * from './error.js';

export * from './backends/memory.js';
export * from './backends/Index.js';
export * from './backends/index/fs.js';
export * from './backends/locked.js';

@@ -8,0 +8,0 @@ export * from './backends/overlay.js';

@@ -5,3 +5,3 @@ export * from './error.js';

export * from './backends/memory.js';
export * from './backends/Index.js';
export * from './backends/index/fs.js';
export * from './backends/locked.js';

@@ -8,0 +8,0 @@ export * from './backends/overlay.js';

@@ -15,3 +15,3 @@ /// <reference types="node" resolution-mode="require"/>

*/
export interface StatsLike {
export interface StatsLike<T extends number | bigint = number | bigint> {
/**

@@ -21,3 +21,3 @@ * Size of the item in bytes.

*/
size: number | bigint;
size: T;
/**

@@ -27,31 +27,31 @@ * Unix-style file mode (e.g. 0o644) that includes the item type

*/
mode: number | bigint;
mode: T;
/**
* time of last access, in milliseconds since epoch
*/
atimeMs: number | bigint;
atimeMs: T;
/**
* time of last modification, in milliseconds since epoch
*/
mtimeMs: number | bigint;
mtimeMs: T;
/**
* time of last time file status was changed, in milliseconds since epoch
*/
ctimeMs: number | bigint;
ctimeMs: T;
/**
* time of file creation, in milliseconds since epoch
*/
birthtimeMs: number | bigint;
birthtimeMs: T;
/**
* the id of the user that owns the file
*/
uid: number | bigint;
uid: T;
/**
* the id of the group that owns the file
*/
gid: number | bigint;
gid: T;
/**
* the ino
*/
ino: number | bigint;
ino: T;
}

@@ -58,0 +58,0 @@ /**

{
"name": "@zenfs/core",
"version": "0.11.2",
"version": "0.12.0",
"description": "A filesystem in your browser",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -5,3 +5,3 @@ #!/usr/bin/env node

import { join } from 'path/posix';
import { resolve } from 'path';
import { relative, resolve } from 'path';
import { minimatch } from 'minimatch';

@@ -42,6 +42,8 @@

function pathToPosix(path) {
function fixSlash(path) {
return path.replaceAll('\\', '/');
}
const resolvedRoot = root || '.';
const colors = {

@@ -71,24 +73,27 @@ reset: 0,

function listing(path, seen = new Set()) {
const entries = new Map();
function computeEntries(path) {
try {
if (options.verbose) console.log(`${color('blue', 'list')} ${path}`);
if (options.ignore.some(pattern => minimatch(path, pattern))) {
if (!options.quiet) console.log(`${color('yellow', 'skip')} ${path}`);
return;
}
const stats = statSync(path);
entries.set('/' + relative(resolvedRoot, path), stats);
if (stats.isFile()) {
if (options.verbose) console.log(`${color('green', 'file')} ${path}`);
return null;
if (options.verbose) {
console.log(`${color('green', 'file')} ${path}`);
}
return;
}
const entries = {};
for (const file of readdirSync(path)) {
const full = join(path, file);
if (options.ignore.some(pattern => minimatch(full, pattern))) {
if (!options.quiet) console.log(`${color('yellow', 'skip')} ${full}`);
continue;
}
entries[file] = listing(full, seen);
computeEntries(join(path, file));
}
if (options.verbose) console.log(`${color('bright_green', ' dir')} ${path}`);
return entries;
if (options.verbose) {
console.log(`${color('bright_green', ' dir')} ${path}`);
}
} catch (e) {

@@ -101,5 +106,12 @@ if (!options.quiet) {

const rootListing = listing(pathToPosix(root));
if (!options.quiet) console.log('Generated listing for ' + pathToPosix(resolve(root)));
computeEntries(resolvedRoot);
if (!options.quiet) {
console.log('Generated listing for ' + fixSlash(resolve(root)));
}
writeFileSync(options.output, JSON.stringify(rootListing));
const index = {
version: 1,
entries: Object.fromEntries(entries),
};
writeFileSync(options.output, JSON.stringify(index));

@@ -1,7 +0,7 @@

import { ErrnoError, Errno } from '../error.js';
import { NoSyncFile } from '../file.js';
import { Errno, ErrnoError } from '../error.js';
import type { FileSystemMetadata } from '../filesystem.js';
import { Stats } from '../stats.js';
import { type ListingTree, FileIndex, type IndexFileInode, AsyncIndexFS } from './Index.js';
import type { Backend } from './backend.js';
import { IndexFS } from './index/fs.js';
import type { IndexData } from './index/index.js';

@@ -41,16 +41,2 @@ /**

/**
* Asynchronously retrieves the size of the given file in bytes.
* @hidden
*/
async function fetchSize(path: string): Promise<number> {
const response = await fetch(path, { method: 'HEAD' }).catch(e => {
throw new ErrnoError(Errno.EIO, e.message);
});
if (!response.ok) {
throw new ErrnoError(Errno.EIO, 'fetch failed: HEAD response returned code ' + response.status);
}
return parseInt(response.headers.get('Content-Length') || '-1', 10);
}
/**
* Configuration options for FetchFS.

@@ -63,3 +49,3 @@ */

*/
index?: string | ListingTree;
index?: string | IndexData;

@@ -73,47 +59,38 @@ /** Used as the URL prefix for fetched files.

/**
* A simple filesystem backed by HTTP using the fetch API.
* A simple filesystem backed by HTTP using the `fetch` API.
*
*
* Listings objects look like the following:
* Index objects look like the following:
*
* ```json
* {
* "home": {
* "jvilk": {
* "someFile.txt": null,
* "someDir": {
* // Empty directory
* }
* }
* }
* "version": 1,
* "entries": {
* "/home": { ... },
* "/home/jvilk": { ... },
* "/home/james": { ... }
* }
* }
* ```
*
* This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.
* Each entry contains the stats associated with the file.
*/
export class FetchFS extends AsyncIndexFS<Stats> {
public readonly prefixUrl: string;
export class FetchFS extends IndexFS {
public readonly baseUrl: string;
protected _init: Promise<void>;
protected async _initialize(index: string | ListingTree): Promise<void> {
if (typeof index != 'string') {
this._index = FileIndex.FromListing(index);
public async ready(): Promise<void> {
if (this._isInitialized) {
return;
}
try {
const response = await fetch(index);
this._index = FileIndex.FromListing((await response.json()) as ListingTree);
} catch (e) {
throw new ErrnoError(Errno.EINVAL, 'Invalid or unavailable file listing tree');
await super.ready();
/**
* Iterate over all of the files and cache their contents
*/
for (const [path, stats] of this.index.files()) {
await this.getData(path, stats);
}
}
public async ready(): Promise<void> {
await this._init;
}
constructor({ index = 'index.json', baseUrl = '' }: FetchOptions) {
super({});
super(typeof index != 'string' ? index : fetchFile<IndexData>(index, 'json'));

@@ -124,5 +101,3 @@ // prefix url must end in a directory separator.

}
this.prefixUrl = baseUrl;
this._init = this._initialize(index);
this.baseUrl = baseUrl;
}

@@ -138,22 +113,15 @@

public empty(): void {
for (const file of this._index.files()) {
delete file.data!.fileData;
}
}
/**
* Special function: Preload the given file into the index.
* Preload the given file into the index.
* @param path
* @param buffer
*/
public preloadFile(path: string, buffer: Uint8Array): void {
const inode = this._index.get(path)!;
if (!inode) {
public preload(path: string, buffer: Uint8Array): void {
const stats = this.index.get(path);
if (!stats) {
throw ErrnoError.With('ENOENT', path, 'preloadFile');
}
if (!inode.isFile()) {
if (!stats.isFile()) {
throw ErrnoError.With('EISDIR', path, 'preloadFile');
}
const stats = inode.data!;
stats.size = buffer.length;

@@ -163,49 +131,22 @@ stats.fileData = buffer;

protected async statFileInode(inode: IndexFileInode<Stats>, path: string): Promise<Stats> {
const stats = inode.data!;
// At this point, a non-opened file will still have default stats from the listing.
if (stats.size < 0) {
stats.size = await this._fetchSize(path);
/**
* @todo Be lazier about actually requesting the data?
*/
protected async getData(path: string, stats: Stats): Promise<Uint8Array> {
if (stats.fileData) {
return stats.fileData;
}
return stats;
const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer');
stats.fileData = data;
return data;
}
protected async openFileInode(inode: IndexFileInode<Stats>, path: string, flag: string): Promise<NoSyncFile<this>> {
const stats = inode.data!;
// Use existing file contents. This maintains the previously-used flag.
protected getDataSync(path: string, stats: Stats): Uint8Array {
if (stats.fileData) {
return new NoSyncFile(this, path, flag, new Stats(stats), stats.fileData);
return stats.fileData;
}
// @todo be lazier about actually requesting the file
const data = await this._fetchFile(path, 'buffer');
// we don't initially have file sizes
stats.size = data.length;
stats.fileData = data;
return new NoSyncFile(this, path, flag, new Stats(stats), data);
}
private _getRemotePath(filePath: string): string {
if (filePath.charAt(0) === '/') {
filePath = filePath.slice(1);
}
return this.prefixUrl + filePath;
throw new ErrnoError(Errno.ENODATA, '', path, 'getData');
}
/**
* Asynchronously download the given file.
*/
protected _fetchFile(path: string, type: 'buffer'): Promise<Uint8Array>;
protected _fetchFile(path: string, type: 'json'): Promise<object>;
protected _fetchFile(path: string, type: 'buffer' | 'json'): Promise<object>;
protected _fetchFile(path: string, type: 'buffer' | 'json'): Promise<object> {
return fetchFile(this._getRemotePath(path), type);
}
/**
* Only requests the HEAD content, for the file size.
*/
protected _fetchSize(path: string): Promise<number> {
return fetchSize(this._getRemotePath(path));
}
}

@@ -212,0 +153,0 @@

@@ -704,4 +704,4 @@ import type { FileReadResult } from 'node:fs/promises';

export class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
constructor(_fs: T, _path: string, _flag: string, _stat: Stats, contents?: Uint8Array) {
super(_fs, _path, _flag, _stat, contents);
constructor(fs: T, path: string, flag: string, stats: Stats, contents?: Uint8Array) {
super(fs, path, flag, stats, contents);
}

@@ -708,0 +708,0 @@ /**

@@ -5,3 +5,3 @@ export * from './error.js';

export * from './backends/memory.js';
export * from './backends/Index.js';
export * from './backends/index/fs.js';
export * from './backends/locked.js';

@@ -8,0 +8,0 @@ export * from './backends/overlay.js';

@@ -17,3 +17,3 @@ import type * as Node from 'fs';

*/
export interface StatsLike {
export interface StatsLike<T extends number | bigint = number | bigint> {
/**

@@ -23,3 +23,3 @@ * Size of the item in bytes.

*/
size: number | bigint;
size: T;
/**

@@ -29,31 +29,31 @@ * Unix-style file mode (e.g. 0o644) that includes the item type

*/
mode: number | bigint;
mode: T;
/**
* time of last access, in milliseconds since epoch
*/
atimeMs: number | bigint;
atimeMs: T;
/**
* time of last modification, in milliseconds since epoch
*/
mtimeMs: number | bigint;
mtimeMs: T;
/**
* time of last time file status was changed, in milliseconds since epoch
*/
ctimeMs: number | bigint;
ctimeMs: T;
/**
* time of file creation, in milliseconds since epoch
*/
birthtimeMs: number | bigint;
birthtimeMs: T;
/**
* the id of the user that owns the file
*/
uid: number | bigint;
uid: T;
/**
* the id of the group that owns the file
*/
gid: number | bigint;
gid: T;
/**
* the ino
*/
ino: number | bigint;
ino: T;
}

@@ -60,0 +60,0 @@

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc