@zenfs/dom 0.0.6 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FileSystemAccess.d.ts +55 -0
- package/dist/FileSystemAccess.js +216 -0
- package/dist/IndexedDB.d.ts +56 -0
- package/dist/IndexedDB.js +201 -0
- package/dist/Storage.d.ts +29 -0
- package/dist/Storage.js +68 -0
- package/dist/browser.min.js +1 -3
- package/dist/browser.min.js.map +4 -4
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3 -5
- package/package.json +7 -7
- package/readme.md +12 -20
- package/dist/backends/FileSystemAccess.d.ts +0 -44
- package/dist/backends/FileSystemAccess.js +0 -208
- package/dist/backends/HTTPRequest.d.ts +0 -85
- package/dist/backends/HTTPRequest.js +0 -202
- package/dist/backends/IndexedDB.d.ts +0 -63
- package/dist/backends/IndexedDB.js +0 -221
- package/dist/backends/Storage.d.ts +0 -39
- package/dist/backends/Storage.js +0 -75
- package/dist/backends/Worker.d.ts +0 -78
- package/dist/backends/Worker.js +0 -163
- package/dist/fetch.d.ts +0 -16
- package/dist/fetch.js +0 -37
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './backends/Storage.js';
|
|
5
|
-
export * from './backends/Worker.js';
|
|
1
|
+
export * from './FileSystemAccess.js';
|
|
2
|
+
export * from './IndexedDB.js';
|
|
3
|
+
export * from './Storage.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './backends/Storage.js';
|
|
5
|
-
export * from './backends/Worker.js';
|
|
1
|
+
export * from './FileSystemAccess.js';
|
|
2
|
+
export * from './IndexedDB.js';
|
|
3
|
+
export * from './Storage.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "DOM backends for ZenFS",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist",
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
"storage"
|
|
11
11
|
],
|
|
12
12
|
"type": "module",
|
|
13
|
-
"homepage": "https://github.com/zen-fs/
|
|
13
|
+
"homepage": "https://github.com/zen-fs/dom",
|
|
14
14
|
"author": "James P. <jp@drvortex.dev> (https://drvortex.dev)",
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/zen-fs/
|
|
18
|
+
"url": "git+https://github.com/zen-fs/dom.git"
|
|
19
19
|
},
|
|
20
20
|
"bugs": {
|
|
21
|
-
"url": "https://github.com/zen-fs/
|
|
21
|
+
"url": "https://github.com/zen-fs/dom/issues"
|
|
22
22
|
},
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">= 18"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"format": "prettier --write src",
|
|
38
38
|
"format:check": "prettier --check src",
|
|
39
|
-
"lint": "eslint src",
|
|
39
|
+
"lint": "tsc -p tsconfig.json --noEmit && eslint src",
|
|
40
40
|
"build": "node scripts/build.mjs",
|
|
41
41
|
"build:docs": "typedoc --out docs --name 'ZenFS DOM' src/index.ts",
|
|
42
42
|
"prepublishOnly": "npm run build"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"typedoc": "^0.25.1",
|
|
51
51
|
"typescript": "5.2.2"
|
|
52
52
|
},
|
|
53
|
-
"
|
|
54
|
-
"@zenfs/core": "^0.
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@zenfs/core": "^0.4.0"
|
|
55
55
|
}
|
|
56
56
|
}
|
package/readme.md
CHANGED
|
@@ -1,36 +1,28 @@
|
|
|
1
1
|
# ZenFS DOM Backends
|
|
2
2
|
|
|
3
|
-
[ZenFS](https://github.com/zen-fs/core) backends for DOM APIs. DOM APIs are
|
|
3
|
+
[ZenFS](https://github.com/zen-fs/core) backends for DOM APIs. DOM APIs are _only_ available natively in browsers.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> [!IMPORTANT]
|
|
6
|
+
> Please read the ZenFS core documentation!
|
|
6
7
|
|
|
7
8
|
## Backends
|
|
8
9
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- `WorkerFS`: Lets you mount the ZenFS file system configured in the main thread in a WebWorker, or the other way around!
|
|
10
|
+
- `Storage`: Stores files in a `Storage` object, like `localStorage` and `sessionStorage`.
|
|
11
|
+
- `IndexedDB`: Stores files into an `IndexedDB` object database.
|
|
12
|
+
- `FileSystemAccess`: Store files using the [Web File System API](https://developer.mozilla.org/Web/API/File_System_API).
|
|
13
13
|
|
|
14
|
-
For more information, see the [API documentation](https://zen-fs.github.io/
|
|
15
|
-
|
|
16
|
-
## Installing
|
|
17
|
-
|
|
18
|
-
```sh
|
|
19
|
-
npm install @zenfs/fs-dom
|
|
20
|
-
```
|
|
14
|
+
For more information, see the [API documentation](https://zen-fs.github.io/dom).
|
|
21
15
|
|
|
22
16
|
## Usage
|
|
23
17
|
|
|
24
|
-
>
|
|
25
|
-
|
|
26
|
-
You can use DOM backends, though you must register them if you plan on using `configure`:
|
|
18
|
+
> [!NOTE]
|
|
19
|
+
> The examples are written in ESM. If you are using CJS, you can `require` the package. If running in a browser you can add a script tag to your HTML pointing to the `browser.min.js` and use ZenFS DOM via the global `ZenFS_DOM` object.
|
|
27
20
|
|
|
28
21
|
```js
|
|
29
|
-
import { configure, fs
|
|
30
|
-
import { Storage } from '@zenfs/
|
|
22
|
+
import { configure, fs } from '@zenfs/core';
|
|
23
|
+
import { Storage } from '@zenfs/dom';
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
await configure({ fs: 'Storage', options: { storage: localStorage } });
|
|
25
|
+
await configure({ backend: Storage, storage: localStorage });
|
|
34
26
|
|
|
35
27
|
if (!fs.existsSync('/test.txt')) {
|
|
36
28
|
fs.writeFileSync('/test.txt', 'This will persist across reloads!');
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Cred } from '@zenfs/core/cred.js';
|
|
2
|
-
import { FileFlag, PreloadFile } from '@zenfs/core/file.js';
|
|
3
|
-
import { BaseFileSystem, FileSystemMetadata } from '@zenfs/core/filesystem.js';
|
|
4
|
-
import { Stats } from '@zenfs/core/stats.js';
|
|
5
|
-
import { type BackendOptions } from '@zenfs/core/backends/backend.js';
|
|
6
|
-
declare global {
|
|
7
|
-
interface FileSystemDirectoryHandle {
|
|
8
|
-
[Symbol.iterator](): IterableIterator<[string, FileSystemHandle]>;
|
|
9
|
-
entries(): IterableIterator<[string, FileSystemHandle]>;
|
|
10
|
-
keys(): IterableIterator<string>;
|
|
11
|
-
values(): IterableIterator<FileSystemHandle>;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
interface FileSystemAccessFileSystemOptions {
|
|
15
|
-
handle: FileSystemDirectoryHandle;
|
|
16
|
-
}
|
|
17
|
-
export declare class FileSystemAccessFile extends PreloadFile<FileSystemAccessFileSystem> {
|
|
18
|
-
constructor(_fs: FileSystemAccessFileSystem, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array);
|
|
19
|
-
sync(): Promise<void>;
|
|
20
|
-
close(): Promise<void>;
|
|
21
|
-
}
|
|
22
|
-
export declare class FileSystemAccessFileSystem extends BaseFileSystem {
|
|
23
|
-
static readonly Name = "FileSystemAccess";
|
|
24
|
-
static Create: any;
|
|
25
|
-
static readonly Options: BackendOptions;
|
|
26
|
-
static isAvailable(): boolean;
|
|
27
|
-
private _handles;
|
|
28
|
-
constructor({ handle }: FileSystemAccessFileSystemOptions);
|
|
29
|
-
get metadata(): FileSystemMetadata;
|
|
30
|
-
_sync(p: string, data: Uint8Array, stats: Stats, cred: Cred): Promise<void>;
|
|
31
|
-
rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
|
|
32
|
-
writeFile(fname: string, data: Uint8Array, flag: FileFlag, mode: number, cred: Cred, createFile?: boolean): Promise<void>;
|
|
33
|
-
createFile(p: string, flag: FileFlag, mode: number, cred: Cred): Promise<FileSystemAccessFile>;
|
|
34
|
-
stat(path: string, cred: Cred): Promise<Stats>;
|
|
35
|
-
exists(p: string, cred: Cred): Promise<boolean>;
|
|
36
|
-
openFile(path: string, flags: FileFlag, cred: Cred): Promise<FileSystemAccessFile>;
|
|
37
|
-
unlink(path: string, cred: Cred): Promise<void>;
|
|
38
|
-
rmdir(path: string, cred: Cred): Promise<void>;
|
|
39
|
-
mkdir(p: string, mode: any, cred: Cred): Promise<void>;
|
|
40
|
-
readdir(path: string, cred: Cred): Promise<string[]>;
|
|
41
|
-
private newFile;
|
|
42
|
-
private getHandle;
|
|
43
|
-
}
|
|
44
|
-
export {};
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
var _a;
|
|
2
|
-
import { basename, dirname, join } from '@zenfs/core/emulation/path.js';
|
|
3
|
-
import { ApiError, ErrorCode } from '@zenfs/core/ApiError.js';
|
|
4
|
-
import { Cred } from '@zenfs/core/cred.js';
|
|
5
|
-
import { FileFlag, PreloadFile } from '@zenfs/core/file.js';
|
|
6
|
-
import { BaseFileSystem } from '@zenfs/core/filesystem.js';
|
|
7
|
-
import { Stats, FileType } from '@zenfs/core/stats.js';
|
|
8
|
-
import { CreateBackend } from '@zenfs/core/backends/backend.js';
|
|
9
|
-
const handleError = (path = '', error) => {
|
|
10
|
-
if (error.name === 'NotFoundError') {
|
|
11
|
-
throw ApiError.ENOENT(path);
|
|
12
|
-
}
|
|
13
|
-
throw error;
|
|
14
|
-
};
|
|
15
|
-
export class FileSystemAccessFile extends PreloadFile {
|
|
16
|
-
constructor(_fs, _path, _flag, _stat, contents) {
|
|
17
|
-
super(_fs, _path, _flag, _stat, contents);
|
|
18
|
-
}
|
|
19
|
-
async sync() {
|
|
20
|
-
if (this.isDirty()) {
|
|
21
|
-
await this._fs._sync(this.getPath(), this.getBuffer(), this.getStats(), Cred.Root);
|
|
22
|
-
this.resetDirty();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async close() {
|
|
26
|
-
await this.sync();
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export class FileSystemAccessFileSystem extends BaseFileSystem {
|
|
30
|
-
static isAvailable() {
|
|
31
|
-
return typeof FileSystemHandle === 'function';
|
|
32
|
-
}
|
|
33
|
-
constructor({ handle }) {
|
|
34
|
-
super();
|
|
35
|
-
this._handles = new Map();
|
|
36
|
-
this._handles.set('/', handle);
|
|
37
|
-
}
|
|
38
|
-
get metadata() {
|
|
39
|
-
return {
|
|
40
|
-
...super.metadata,
|
|
41
|
-
name: _a.Name,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
async _sync(p, data, stats, cred) {
|
|
45
|
-
const currentStats = await this.stat(p, cred);
|
|
46
|
-
if (stats.mtime !== currentStats.mtime) {
|
|
47
|
-
await this.writeFile(p, data, FileFlag.getFileFlag('w'), currentStats.mode, cred);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async rename(oldPath, newPath, cred) {
|
|
51
|
-
try {
|
|
52
|
-
const handle = await this.getHandle(oldPath);
|
|
53
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
54
|
-
const files = await this.readdir(oldPath, cred);
|
|
55
|
-
await this.mkdir(newPath, 'wx', cred);
|
|
56
|
-
if (files.length === 0) {
|
|
57
|
-
await this.unlink(oldPath, cred);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
for (const file of files) {
|
|
61
|
-
await this.rename(join(oldPath, file), join(newPath, file), cred);
|
|
62
|
-
await this.unlink(oldPath, cred);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (handle instanceof FileSystemFileHandle) {
|
|
67
|
-
const oldFile = await handle.getFile(), destFolder = await this.getHandle(dirname(newPath));
|
|
68
|
-
if (destFolder instanceof FileSystemDirectoryHandle) {
|
|
69
|
-
const newFile = await destFolder.getFileHandle(basename(newPath), { create: true });
|
|
70
|
-
const writable = await newFile.createWritable();
|
|
71
|
-
const buffer = await oldFile.arrayBuffer();
|
|
72
|
-
await writable.write(buffer);
|
|
73
|
-
writable.close();
|
|
74
|
-
await this.unlink(oldPath, cred);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
catch (err) {
|
|
79
|
-
handleError(oldPath, err);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
async writeFile(fname, data, flag, mode, cred, createFile) {
|
|
83
|
-
const handle = await this.getHandle(dirname(fname));
|
|
84
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
85
|
-
const file = await handle.getFileHandle(basename(fname), { create: true });
|
|
86
|
-
const writable = await file.createWritable();
|
|
87
|
-
await writable.write(data);
|
|
88
|
-
await writable.close();
|
|
89
|
-
//return createFile ? this.newFile(fname, flag, data) : undefined;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async createFile(p, flag, mode, cred) {
|
|
93
|
-
await this.writeFile(p, new Uint8Array(), flag, mode, cred, true);
|
|
94
|
-
return this.openFile(p, flag, cred);
|
|
95
|
-
}
|
|
96
|
-
async stat(path, cred) {
|
|
97
|
-
const handle = await this.getHandle(path);
|
|
98
|
-
if (!handle) {
|
|
99
|
-
throw ApiError.FileError(ErrorCode.EINVAL, path);
|
|
100
|
-
}
|
|
101
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
102
|
-
return new Stats(FileType.DIRECTORY, 4096);
|
|
103
|
-
}
|
|
104
|
-
if (handle instanceof FileSystemFileHandle) {
|
|
105
|
-
const { lastModified, size } = await handle.getFile();
|
|
106
|
-
return new Stats(FileType.FILE, size, undefined, undefined, lastModified);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async exists(p, cred) {
|
|
110
|
-
try {
|
|
111
|
-
await this.getHandle(p);
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
catch (e) {
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
async openFile(path, flags, cred) {
|
|
119
|
-
const handle = await this.getHandle(path);
|
|
120
|
-
if (handle instanceof FileSystemFileHandle) {
|
|
121
|
-
const file = await handle.getFile();
|
|
122
|
-
const buffer = await file.arrayBuffer();
|
|
123
|
-
return this.newFile(path, flags, buffer, file.size, file.lastModified);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
async unlink(path, cred) {
|
|
127
|
-
const handle = await this.getHandle(dirname(path));
|
|
128
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
129
|
-
try {
|
|
130
|
-
await handle.removeEntry(basename(path), { recursive: true });
|
|
131
|
-
}
|
|
132
|
-
catch (e) {
|
|
133
|
-
handleError(path, e);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async rmdir(path, cred) {
|
|
138
|
-
return this.unlink(path, cred);
|
|
139
|
-
}
|
|
140
|
-
async mkdir(p, mode, cred) {
|
|
141
|
-
const overwrite = mode && mode.flag && mode.flag.includes('w') && !mode.flag.includes('x');
|
|
142
|
-
const existingHandle = await this.getHandle(p);
|
|
143
|
-
if (existingHandle && !overwrite) {
|
|
144
|
-
throw ApiError.EEXIST(p);
|
|
145
|
-
}
|
|
146
|
-
const handle = await this.getHandle(dirname(p));
|
|
147
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
148
|
-
await handle.getDirectoryHandle(basename(p), { create: true });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
async readdir(path, cred) {
|
|
152
|
-
const handle = await this.getHandle(path);
|
|
153
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
154
|
-
throw ApiError.ENOTDIR(path);
|
|
155
|
-
}
|
|
156
|
-
const _keys = [];
|
|
157
|
-
for await (const key of handle.keys()) {
|
|
158
|
-
_keys.push(join(path, key));
|
|
159
|
-
}
|
|
160
|
-
return _keys;
|
|
161
|
-
}
|
|
162
|
-
newFile(path, flag, data, size, lastModified) {
|
|
163
|
-
return new FileSystemAccessFile(this, path, flag, new Stats(FileType.FILE, size || 0, undefined, undefined, lastModified || new Date().getTime()), new Uint8Array(data));
|
|
164
|
-
}
|
|
165
|
-
async getHandle(path) {
|
|
166
|
-
if (this._handles.has(path)) {
|
|
167
|
-
return this._handles.get(path);
|
|
168
|
-
}
|
|
169
|
-
let walkedPath = '/';
|
|
170
|
-
const [, ...pathParts] = path.split('/');
|
|
171
|
-
const getHandleParts = async ([pathPart, ...remainingPathParts]) => {
|
|
172
|
-
const walkingPath = join(walkedPath, pathPart);
|
|
173
|
-
const continueWalk = (handle) => {
|
|
174
|
-
walkedPath = walkingPath;
|
|
175
|
-
this._handles.set(walkedPath, handle);
|
|
176
|
-
if (remainingPathParts.length === 0) {
|
|
177
|
-
return this._handles.get(path);
|
|
178
|
-
}
|
|
179
|
-
getHandleParts(remainingPathParts);
|
|
180
|
-
};
|
|
181
|
-
const handle = this._handles.get(walkedPath);
|
|
182
|
-
try {
|
|
183
|
-
return await continueWalk(await handle.getDirectoryHandle(pathPart));
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
if (error.name === 'TypeMismatchError') {
|
|
187
|
-
try {
|
|
188
|
-
return await continueWalk(await handle.getFileHandle(pathPart));
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
handleError(walkingPath, err);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
else if (error.message === 'Name is not allowed.') {
|
|
195
|
-
throw new ApiError(ErrorCode.ENOENT, error.message, walkingPath);
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
handleError(walkingPath, error);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
await getHandleParts(pathParts);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
_a = FileSystemAccessFileSystem;
|
|
206
|
-
FileSystemAccessFileSystem.Name = 'FileSystemAccess';
|
|
207
|
-
FileSystemAccessFileSystem.Create = CreateBackend.bind(_a);
|
|
208
|
-
FileSystemAccessFileSystem.Options = {};
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { BaseFileSystem, FileSystemMetadata } from '@zenfs/core/filesystem.js';
|
|
2
|
-
import { FileFlag, NoSyncFile } from '@zenfs/core/file.js';
|
|
3
|
-
import { Stats } from '@zenfs/core/stats.js';
|
|
4
|
-
import { Cred } from '@zenfs/core/cred.js';
|
|
5
|
-
import { type BackendOptions } from '@zenfs/core/backends/backend.js';
|
|
6
|
-
export interface HTTPRequestIndex {
|
|
7
|
-
[key: string]: string;
|
|
8
|
-
}
|
|
9
|
-
export declare namespace HTTPRequest {
|
|
10
|
-
/**
|
|
11
|
-
* Configuration options for a HTTPRequest file system.
|
|
12
|
-
*/
|
|
13
|
-
interface Options {
|
|
14
|
-
/**
|
|
15
|
-
* URL to a file index as a JSON file or the file index object itself, generated with the make_http_index script.
|
|
16
|
-
* Defaults to `index.json`.
|
|
17
|
-
*/
|
|
18
|
-
index?: string | HTTPRequestIndex;
|
|
19
|
-
/** Used as the URL prefix for fetched files.
|
|
20
|
-
* Default: Fetch files relative to the index.
|
|
21
|
-
*/
|
|
22
|
-
baseUrl?: string;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* A simple filesystem backed by HTTP downloads. You must create a directory listing using the
|
|
27
|
-
* `make_http_index` tool provided by ZenFS.
|
|
28
|
-
*
|
|
29
|
-
* If you install ZenFS globally with `npm i -g zenfs`, you can generate a listing by
|
|
30
|
-
* running `make_http_index` in your terminal in the directory you would like to index:
|
|
31
|
-
*
|
|
32
|
-
* ```
|
|
33
|
-
* make_http_index > index.json
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* Listings objects look like the following:
|
|
37
|
-
*
|
|
38
|
-
* ```json
|
|
39
|
-
* {
|
|
40
|
-
* "home": {
|
|
41
|
-
* "jvilk": {
|
|
42
|
-
* "someFile.txt": null,
|
|
43
|
-
* "someDir": {
|
|
44
|
-
* // Empty directory
|
|
45
|
-
* }
|
|
46
|
-
* }
|
|
47
|
-
* }
|
|
48
|
-
* }
|
|
49
|
-
* ```
|
|
50
|
-
*
|
|
51
|
-
* *This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.*
|
|
52
|
-
*/
|
|
53
|
-
export declare class HTTPRequest extends BaseFileSystem {
|
|
54
|
-
static readonly Name = "HTTPRequest";
|
|
55
|
-
static Create: any;
|
|
56
|
-
static readonly Options: BackendOptions;
|
|
57
|
-
static isAvailable(): boolean;
|
|
58
|
-
readonly prefixUrl: string;
|
|
59
|
-
private _index;
|
|
60
|
-
constructor({ index, baseUrl }: HTTPRequest.Options);
|
|
61
|
-
get metadata(): FileSystemMetadata;
|
|
62
|
-
empty(): void;
|
|
63
|
-
/**
|
|
64
|
-
* Special HTTPFS function: Preload the given file into the index.
|
|
65
|
-
* @param path
|
|
66
|
-
* @param buffer
|
|
67
|
-
*/
|
|
68
|
-
preloadFile(path: string, buffer: Uint8Array): void;
|
|
69
|
-
stat(path: string, cred: Cred): Promise<Stats>;
|
|
70
|
-
open(path: string, flags: FileFlag, mode: number, cred: Cred): Promise<NoSyncFile<this>>;
|
|
71
|
-
readdir(path: string, cred: Cred): Promise<string[]>;
|
|
72
|
-
/**
|
|
73
|
-
* We have the entire file as a buffer; optimize readFile.
|
|
74
|
-
*/
|
|
75
|
-
readFile(fname: string, flag: FileFlag, cred: Cred): Promise<Uint8Array>;
|
|
76
|
-
private _getHTTPPath;
|
|
77
|
-
/**
|
|
78
|
-
* Asynchronously download the given file.
|
|
79
|
-
*/
|
|
80
|
-
private _requestFile;
|
|
81
|
-
/**
|
|
82
|
-
* Only requests the HEAD content, for the file size.
|
|
83
|
-
*/
|
|
84
|
-
private _requestFileSize;
|
|
85
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
var _a;
|
|
2
|
-
import { BaseFileSystem } from '@zenfs/core/filesystem.js';
|
|
3
|
-
import { ApiError, ErrorCode } from '@zenfs/core/ApiError.js';
|
|
4
|
-
import { ActionType, NoSyncFile } from '@zenfs/core/file.js';
|
|
5
|
-
import { Stats } from '@zenfs/core/stats.js';
|
|
6
|
-
import { fetchIsAvailable, fetchFile, fetchFileSize } from '../fetch.js';
|
|
7
|
-
import { FileIndex, isIndexFileInode, isIndexDirInode } from '@zenfs/core/FileIndex.js';
|
|
8
|
-
import { CreateBackend } from '@zenfs/core/backends/backend.js';
|
|
9
|
-
import { R_OK } from '@zenfs/core/emulation/constants.js';
|
|
10
|
-
/**
|
|
11
|
-
* A simple filesystem backed by HTTP downloads. You must create a directory listing using the
|
|
12
|
-
* `make_http_index` tool provided by ZenFS.
|
|
13
|
-
*
|
|
14
|
-
* If you install ZenFS globally with `npm i -g zenfs`, you can generate a listing by
|
|
15
|
-
* running `make_http_index` in your terminal in the directory you would like to index:
|
|
16
|
-
*
|
|
17
|
-
* ```
|
|
18
|
-
* make_http_index > index.json
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* Listings objects look like the following:
|
|
22
|
-
*
|
|
23
|
-
* ```json
|
|
24
|
-
* {
|
|
25
|
-
* "home": {
|
|
26
|
-
* "jvilk": {
|
|
27
|
-
* "someFile.txt": null,
|
|
28
|
-
* "someDir": {
|
|
29
|
-
* // Empty directory
|
|
30
|
-
* }
|
|
31
|
-
* }
|
|
32
|
-
* }
|
|
33
|
-
* }
|
|
34
|
-
* ```
|
|
35
|
-
*
|
|
36
|
-
* *This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.*
|
|
37
|
-
*/
|
|
38
|
-
export class HTTPRequest extends BaseFileSystem {
|
|
39
|
-
static isAvailable() {
|
|
40
|
-
return fetchIsAvailable;
|
|
41
|
-
}
|
|
42
|
-
constructor({ index, baseUrl = '' }) {
|
|
43
|
-
super();
|
|
44
|
-
if (!index) {
|
|
45
|
-
index = 'index.json';
|
|
46
|
-
}
|
|
47
|
-
const indexRequest = typeof index == 'string' ? fetchFile(index, 'json') : Promise.resolve(index);
|
|
48
|
-
this._ready = indexRequest.then(data => {
|
|
49
|
-
this._index = FileIndex.fromListing(data);
|
|
50
|
-
return this;
|
|
51
|
-
});
|
|
52
|
-
// prefix_url must end in a directory separator.
|
|
53
|
-
if (baseUrl.length > 0 && baseUrl.charAt(baseUrl.length - 1) !== '/') {
|
|
54
|
-
baseUrl = baseUrl + '/';
|
|
55
|
-
}
|
|
56
|
-
this.prefixUrl = baseUrl;
|
|
57
|
-
}
|
|
58
|
-
get metadata() {
|
|
59
|
-
return {
|
|
60
|
-
...super.metadata,
|
|
61
|
-
name: _a.Name,
|
|
62
|
-
readonly: true,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
empty() {
|
|
66
|
-
this._index.fileIterator(function (file) {
|
|
67
|
-
file.fileData = null;
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Special HTTPFS function: Preload the given file into the index.
|
|
72
|
-
* @param path
|
|
73
|
-
* @param buffer
|
|
74
|
-
*/
|
|
75
|
-
preloadFile(path, buffer) {
|
|
76
|
-
const inode = this._index.getInode(path);
|
|
77
|
-
if (isIndexFileInode(inode)) {
|
|
78
|
-
if (inode === null) {
|
|
79
|
-
throw ApiError.ENOENT(path);
|
|
80
|
-
}
|
|
81
|
-
const stats = inode.getData();
|
|
82
|
-
stats.size = buffer.length;
|
|
83
|
-
stats.fileData = buffer;
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
throw ApiError.EISDIR(path);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
async stat(path, cred) {
|
|
90
|
-
const inode = this._index.getInode(path);
|
|
91
|
-
if (inode === null) {
|
|
92
|
-
throw ApiError.ENOENT(path);
|
|
93
|
-
}
|
|
94
|
-
if (!inode.toStats().hasAccess(R_OK, cred)) {
|
|
95
|
-
throw ApiError.EACCES(path);
|
|
96
|
-
}
|
|
97
|
-
let stats;
|
|
98
|
-
if (isIndexFileInode(inode)) {
|
|
99
|
-
stats = inode.getData();
|
|
100
|
-
// At this point, a non-opened file will still have default stats from the listing.
|
|
101
|
-
if (stats.size < 0) {
|
|
102
|
-
stats.size = await this._requestFileSize(path);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
else if (isIndexDirInode(inode)) {
|
|
106
|
-
stats = inode.getStats();
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
throw ApiError.FileError(ErrorCode.EINVAL, path);
|
|
110
|
-
}
|
|
111
|
-
return stats;
|
|
112
|
-
}
|
|
113
|
-
async open(path, flags, mode, cred) {
|
|
114
|
-
// INVARIANT: You can't write to files on this file system.
|
|
115
|
-
if (flags.isWriteable()) {
|
|
116
|
-
throw new ApiError(ErrorCode.EPERM, path);
|
|
117
|
-
}
|
|
118
|
-
// Check if the path exists, and is a file.
|
|
119
|
-
const inode = this._index.getInode(path);
|
|
120
|
-
if (inode === null) {
|
|
121
|
-
throw ApiError.ENOENT(path);
|
|
122
|
-
}
|
|
123
|
-
if (!inode.toStats().hasAccess(flags.getMode(), cred)) {
|
|
124
|
-
throw ApiError.EACCES(path);
|
|
125
|
-
}
|
|
126
|
-
if (isIndexFileInode(inode) || isIndexDirInode(inode)) {
|
|
127
|
-
switch (flags.pathExistsAction()) {
|
|
128
|
-
case ActionType.THROW_EXCEPTION:
|
|
129
|
-
case ActionType.TRUNCATE_FILE:
|
|
130
|
-
throw ApiError.EEXIST(path);
|
|
131
|
-
case ActionType.NOP:
|
|
132
|
-
if (isIndexDirInode(inode)) {
|
|
133
|
-
const stats = inode.getStats();
|
|
134
|
-
return new NoSyncFile(this, path, flags, stats, stats.fileData || undefined);
|
|
135
|
-
}
|
|
136
|
-
const stats = inode.getData();
|
|
137
|
-
// Use existing file contents.
|
|
138
|
-
// XXX: Uh, this maintains the previously-used flag.
|
|
139
|
-
if (stats.fileData) {
|
|
140
|
-
return new NoSyncFile(this, path, flags, Stats.clone(stats), stats.fileData);
|
|
141
|
-
}
|
|
142
|
-
// @todo be lazier about actually requesting the file
|
|
143
|
-
const buffer = await this._requestFile(path, 'buffer');
|
|
144
|
-
// we don't initially have file sizes
|
|
145
|
-
stats.size = buffer.length;
|
|
146
|
-
stats.fileData = buffer;
|
|
147
|
-
return new NoSyncFile(this, path, flags, Stats.clone(stats), buffer);
|
|
148
|
-
default:
|
|
149
|
-
throw new ApiError(ErrorCode.EINVAL, 'Invalid FileMode object.');
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
else {
|
|
153
|
-
throw ApiError.EPERM(path);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
async readdir(path, cred) {
|
|
157
|
-
return this.readdirSync(path, cred);
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* We have the entire file as a buffer; optimize readFile.
|
|
161
|
-
*/
|
|
162
|
-
async readFile(fname, flag, cred) {
|
|
163
|
-
// Get file.
|
|
164
|
-
const fd = await this.open(fname, flag, 0o644, cred);
|
|
165
|
-
try {
|
|
166
|
-
return fd.getBuffer();
|
|
167
|
-
}
|
|
168
|
-
finally {
|
|
169
|
-
await fd.close();
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
_getHTTPPath(filePath) {
|
|
173
|
-
if (filePath.charAt(0) === '/') {
|
|
174
|
-
filePath = filePath.slice(1);
|
|
175
|
-
}
|
|
176
|
-
return this.prefixUrl + filePath;
|
|
177
|
-
}
|
|
178
|
-
_requestFile(p, type) {
|
|
179
|
-
return fetchFile(this._getHTTPPath(p), type);
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Only requests the HEAD content, for the file size.
|
|
183
|
-
*/
|
|
184
|
-
_requestFileSize(path) {
|
|
185
|
-
return fetchFileSize(this._getHTTPPath(path));
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
_a = HTTPRequest;
|
|
189
|
-
HTTPRequest.Name = 'HTTPRequest';
|
|
190
|
-
HTTPRequest.Create = CreateBackend.bind(_a);
|
|
191
|
-
HTTPRequest.Options = {
|
|
192
|
-
index: {
|
|
193
|
-
type: ['string', 'object'],
|
|
194
|
-
optional: true,
|
|
195
|
-
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make_http_index script. Defaults to `index.json`.',
|
|
196
|
-
},
|
|
197
|
-
baseUrl: {
|
|
198
|
-
type: 'string',
|
|
199
|
-
optional: true,
|
|
200
|
-
description: 'Used as the URL prefix for fetched files. Default: Fetch files relative to the index.',
|
|
201
|
-
},
|
|
202
|
-
};
|