@zenfs/dom 1.0.1 → 1.0.3
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/IndexedDB.d.ts +1 -0
- package/dist/IndexedDB.js +4 -1
- package/dist/Storage.d.ts +4 -0
- package/dist/Storage.js +6 -0
- package/dist/access.d.ts +0 -8
- package/dist/access.js +5 -1
- package/package.json +11 -3
- package/src/IndexedDB.ts +0 -170
- package/src/Storage.ts +0 -90
- package/src/access.ts +0 -237
- package/src/index.ts +0 -3
- package/src/utils.ts +0 -74
package/dist/IndexedDB.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare class IndexedDBTransaction extends AsyncTransaction<IndexedDBStor
|
|
|
8
8
|
store: IndexedDBStore;
|
|
9
9
|
private _idb;
|
|
10
10
|
constructor(tx: IDBTransaction, store: IndexedDBStore);
|
|
11
|
+
keys(): Promise<Iterable<Ino>>;
|
|
11
12
|
get(key: Ino): Promise<Uint8Array>;
|
|
12
13
|
set(key: Ino, data: Uint8Array): Promise<void>;
|
|
13
14
|
remove(key: Ino): Promise<void>;
|
package/dist/IndexedDB.js
CHANGED
|
@@ -19,6 +19,9 @@ export class IndexedDBTransaction extends AsyncTransaction {
|
|
|
19
19
|
this.store = store;
|
|
20
20
|
this._idb = tx.objectStore(store.name);
|
|
21
21
|
}
|
|
22
|
+
async keys() {
|
|
23
|
+
return (await wrap(this._idb.getAllKeys())).filter(k => typeof k == 'string').map(k => BigInt(k));
|
|
24
|
+
}
|
|
22
25
|
get(key) {
|
|
23
26
|
return wrap(this._idb.get(key.toString()));
|
|
24
27
|
}
|
|
@@ -69,7 +72,7 @@ export class IndexedDBStore {
|
|
|
69
72
|
this.db = db;
|
|
70
73
|
}
|
|
71
74
|
sync() {
|
|
72
|
-
throw
|
|
75
|
+
throw ErrnoError.With('ENOSYS', undefined, 'IndexedDBStore.sync');
|
|
73
76
|
}
|
|
74
77
|
get name() {
|
|
75
78
|
return this.db.name;
|
package/dist/Storage.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class WebStorageStore implements Store, SimpleSyncStore {
|
|
|
11
11
|
clearSync(): void;
|
|
12
12
|
sync(): Promise<void>;
|
|
13
13
|
transaction(): SimpleTransaction;
|
|
14
|
+
keys(): Iterable<Ino>;
|
|
14
15
|
get(key: Ino): Uint8Array | undefined;
|
|
15
16
|
set(key: Ino, data: Uint8Array): void;
|
|
16
17
|
delete(key: Ino): void;
|
|
@@ -36,6 +37,9 @@ declare const _WebStorage: {
|
|
|
36
37
|
readonly description: "The Storage to use. Defaults to globalThis.localStorage.";
|
|
37
38
|
};
|
|
38
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* @todo Consider replacing `instanceof` with a duck-typing check?
|
|
42
|
+
*/
|
|
39
43
|
readonly isAvailable: (storage?: Storage) => boolean;
|
|
40
44
|
readonly create: ({ storage }: WebStorageOptions) => StoreFS<WebStorageStore>;
|
|
41
45
|
};
|
package/dist/Storage.js
CHANGED
|
@@ -20,6 +20,9 @@ export class WebStorageStore {
|
|
|
20
20
|
// No need to differentiate.
|
|
21
21
|
return new SimpleTransaction(this);
|
|
22
22
|
}
|
|
23
|
+
keys() {
|
|
24
|
+
return Object.keys(this.storage).map(k => BigInt(k));
|
|
25
|
+
}
|
|
23
26
|
get(key) {
|
|
24
27
|
const data = this.storage.getItem(key.toString());
|
|
25
28
|
if (typeof data != 'string') {
|
|
@@ -56,6 +59,9 @@ const _WebStorage = {
|
|
|
56
59
|
description: 'The Storage to use. Defaults to globalThis.localStorage.',
|
|
57
60
|
},
|
|
58
61
|
},
|
|
62
|
+
/**
|
|
63
|
+
* @todo Consider replacing `instanceof` with a duck-typing check?
|
|
64
|
+
*/
|
|
59
65
|
isAvailable(storage = globalThis.localStorage) {
|
|
60
66
|
return storage instanceof globalThis.Storage;
|
|
61
67
|
},
|
package/dist/access.d.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import type { FileSystemMetadata } from '@zenfs/core';
|
|
2
2
|
import { FileSystem, PreloadFile, Stats } from '@zenfs/core';
|
|
3
|
-
declare global {
|
|
4
|
-
interface FileSystemDirectoryHandle {
|
|
5
|
-
[Symbol.iterator](): IterableIterator<[string, FileSystemHandle]>;
|
|
6
|
-
entries(): IterableIterator<[string, FileSystemHandle]>;
|
|
7
|
-
keys(): IterableIterator<string>;
|
|
8
|
-
values(): IterableIterator<FileSystemHandle>;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
3
|
export interface WebAccessOptions {
|
|
12
4
|
handle: FileSystemDirectoryHandle;
|
|
13
5
|
}
|
package/dist/access.js
CHANGED
|
@@ -137,7 +137,11 @@ export class WebAccessFS extends Async(FileSystem) {
|
|
|
137
137
|
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
138
138
|
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
const entries = [];
|
|
141
|
+
for await (const k of handle.keys()) {
|
|
142
|
+
entries.push(k);
|
|
143
|
+
}
|
|
144
|
+
return entries;
|
|
141
145
|
}
|
|
142
146
|
async getHandle(path) {
|
|
143
147
|
if (this._handles.has(path)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/dom",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "DOM backends for ZenFS",
|
|
5
|
+
"funding": {
|
|
6
|
+
"type": "individual",
|
|
7
|
+
"url": "https://github.com/sponsors/james-pre"
|
|
8
|
+
},
|
|
5
9
|
"main": "dist/index.js",
|
|
6
10
|
"types": "dist/index.d.ts",
|
|
7
11
|
"type": "module",
|
|
@@ -17,7 +21,6 @@
|
|
|
17
21
|
},
|
|
18
22
|
"files": [
|
|
19
23
|
"dist",
|
|
20
|
-
"src",
|
|
21
24
|
"license.md",
|
|
22
25
|
"tsconfig.json"
|
|
23
26
|
],
|
|
@@ -42,12 +45,17 @@
|
|
|
42
45
|
"eslint": "^9.12.0",
|
|
43
46
|
"globals": "^15.10.0",
|
|
44
47
|
"prettier": "^3.2.5",
|
|
48
|
+
"tsx": "^4.19.2",
|
|
45
49
|
"typedoc": "^0.26.10",
|
|
46
50
|
"typescript": "^5.5.0",
|
|
47
51
|
"typescript-eslint": "^8.8.1"
|
|
48
52
|
},
|
|
49
53
|
"peerDependencies": {
|
|
50
|
-
"@zenfs/core": "^1.
|
|
54
|
+
"@zenfs/core": "^1.2.0"
|
|
55
|
+
},
|
|
56
|
+
"optionalDependencies": {
|
|
57
|
+
"fake-indexeddb": "^6.0.0",
|
|
58
|
+
"file-system-access": "^1.0.4"
|
|
51
59
|
},
|
|
52
60
|
"keywords": [
|
|
53
61
|
"filesystem",
|
package/src/IndexedDB.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import type { Backend, Ino, SharedConfig, Store } from '@zenfs/core';
|
|
2
|
-
import { Async, AsyncTransaction, ErrnoError, InMemory, StoreFS } from '@zenfs/core';
|
|
3
|
-
import { convertException } from './utils.js';
|
|
4
|
-
|
|
5
|
-
function wrap<T>(request: IDBRequest<T>): Promise<T> {
|
|
6
|
-
return new Promise((resolve, reject) => {
|
|
7
|
-
request.onsuccess = () => resolve(request.result);
|
|
8
|
-
request.onerror = e => {
|
|
9
|
-
e.preventDefault();
|
|
10
|
-
reject(convertException(request.error!));
|
|
11
|
-
};
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @hidden
|
|
17
|
-
*/
|
|
18
|
-
export class IndexedDBTransaction extends AsyncTransaction<IndexedDBStore> {
|
|
19
|
-
private _idb: IDBObjectStore;
|
|
20
|
-
|
|
21
|
-
public constructor(
|
|
22
|
-
public tx: IDBTransaction,
|
|
23
|
-
public store: IndexedDBStore
|
|
24
|
-
) {
|
|
25
|
-
super(store);
|
|
26
|
-
this._idb = tx.objectStore(store.name);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public get(key: Ino): Promise<Uint8Array> {
|
|
30
|
-
return wrap(this._idb.get(key.toString()));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public async set(key: Ino, data: Uint8Array): Promise<void> {
|
|
34
|
-
await wrap(this._idb.put(data, key.toString()));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
public remove(key: Ino): Promise<void> {
|
|
38
|
-
return wrap(this._idb.delete(key.toString()));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public async commit(): Promise<void> {
|
|
42
|
-
if (this.done) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
46
|
-
this.done = true;
|
|
47
|
-
this.tx.oncomplete = () => resolve();
|
|
48
|
-
this.tx.onerror = () => reject(convertException(this.tx.error!));
|
|
49
|
-
this.tx.commit();
|
|
50
|
-
return promise;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public async abort(): Promise<void> {
|
|
54
|
-
if (this.done) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
this.done = true;
|
|
58
|
-
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
59
|
-
this.tx.onabort = () => resolve();
|
|
60
|
-
this.tx.onerror = () => reject(convertException(this.tx.error!));
|
|
61
|
-
this.tx.abort();
|
|
62
|
-
return promise;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function createDB(name: string, indexedDB: IDBFactory = globalThis.indexedDB): Promise<IDBDatabase> {
|
|
67
|
-
const req: IDBOpenDBRequest = indexedDB.open(name);
|
|
68
|
-
|
|
69
|
-
req.onupgradeneeded = () => {
|
|
70
|
-
const db: IDBDatabase = req.result;
|
|
71
|
-
// This should never happen; we're at version 1. Why does another database exist?
|
|
72
|
-
if (db.objectStoreNames.contains(name)) {
|
|
73
|
-
db.deleteObjectStore(name);
|
|
74
|
-
}
|
|
75
|
-
db.createObjectStore(name);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const result = await wrap(req);
|
|
79
|
-
return result;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export class IndexedDBStore implements Store {
|
|
83
|
-
public constructor(protected db: IDBDatabase) {}
|
|
84
|
-
|
|
85
|
-
public sync(): Promise<void> {
|
|
86
|
-
throw new Error('Method not implemented.');
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
public get name(): string {
|
|
90
|
-
return this.db.name;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
public clear(): Promise<void> {
|
|
94
|
-
return wrap(this.db.transaction(this.name, 'readwrite').objectStore(this.name).clear());
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
public clearSync(): void {
|
|
98
|
-
throw ErrnoError.With('ENOSYS', undefined, 'IndexedDBStore.clearSync');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
public transaction(): IndexedDBTransaction {
|
|
102
|
-
const tx = this.db.transaction(this.name, 'readwrite');
|
|
103
|
-
return new IndexedDBTransaction(tx, this);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Configuration options for the IndexedDB file system.
|
|
109
|
-
*/
|
|
110
|
-
export interface IndexedDBOptions {
|
|
111
|
-
/**
|
|
112
|
-
* The name of this file system. You can have multiple IndexedDB file systems operating at once, but each must have a different name.
|
|
113
|
-
*/
|
|
114
|
-
storeName?: string;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* The IDBFactory to use. Defaults to `globalThis.indexedDB`.
|
|
118
|
-
*/
|
|
119
|
-
idbFactory?: IDBFactory;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* A file system that uses the IndexedDB key value file system.
|
|
124
|
-
*/
|
|
125
|
-
|
|
126
|
-
const _IndexedDB = {
|
|
127
|
-
name: 'IndexedDB',
|
|
128
|
-
|
|
129
|
-
options: {
|
|
130
|
-
storeName: {
|
|
131
|
-
type: 'string',
|
|
132
|
-
required: false,
|
|
133
|
-
description: 'The name of this file system. You can have multiple IndexedDB file systems operating at once, but each must have a different name.',
|
|
134
|
-
},
|
|
135
|
-
idbFactory: {
|
|
136
|
-
type: 'object',
|
|
137
|
-
required: false,
|
|
138
|
-
description: 'The IDBFactory to use. Defaults to globalThis.indexedDB.',
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
|
|
142
|
-
async isAvailable(idbFactory: IDBFactory = globalThis.indexedDB): Promise<boolean> {
|
|
143
|
-
try {
|
|
144
|
-
if (!(idbFactory instanceof IDBFactory)) {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
const req = idbFactory.open('__zenfs_test');
|
|
148
|
-
await wrap(req);
|
|
149
|
-
idbFactory.deleteDatabase('__zenfs_test');
|
|
150
|
-
return true;
|
|
151
|
-
} catch (e) {
|
|
152
|
-
idbFactory.deleteDatabase('__zenfs_test');
|
|
153
|
-
return false;
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
async create(options: IndexedDBOptions & Partial<SharedConfig>) {
|
|
158
|
-
const db = await createDB(options.storeName || 'zenfs', options.idbFactory);
|
|
159
|
-
const store = new IndexedDBStore(db);
|
|
160
|
-
const fs = new (Async(StoreFS))(store);
|
|
161
|
-
if (!options?.disableAsyncCache) {
|
|
162
|
-
fs._sync = InMemory.create({ name: 'idb-cache' });
|
|
163
|
-
}
|
|
164
|
-
return fs;
|
|
165
|
-
},
|
|
166
|
-
} as const satisfies Backend<StoreFS, IndexedDBOptions>;
|
|
167
|
-
type _IndexedDB = typeof _IndexedDB;
|
|
168
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
169
|
-
interface IndexedDB extends _IndexedDB {}
|
|
170
|
-
export const IndexedDB: IndexedDB = _IndexedDB;
|
package/src/Storage.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import type { Backend, Ino, SimpleSyncStore, Store } from '@zenfs/core';
|
|
2
|
-
import { ErrnoError, Errno, SimpleTransaction, StoreFS, decodeRaw, encodeRaw } from '@zenfs/core';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A synchronous key-value store backed by Storage.
|
|
6
|
-
*/
|
|
7
|
-
export class WebStorageStore implements Store, SimpleSyncStore {
|
|
8
|
-
public get name(): string {
|
|
9
|
-
return WebStorage.name;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
public constructor(protected storage: Storage) {}
|
|
13
|
-
|
|
14
|
-
public clear(): void {
|
|
15
|
-
this.storage.clear();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
public clearSync(): void {
|
|
19
|
-
this.storage.clear();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
public async sync(): Promise<void> {}
|
|
23
|
-
|
|
24
|
-
public transaction(): SimpleTransaction {
|
|
25
|
-
// No need to differentiate.
|
|
26
|
-
return new SimpleTransaction(this);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public get(key: Ino): Uint8Array | undefined {
|
|
30
|
-
const data = this.storage.getItem(key.toString());
|
|
31
|
-
if (typeof data != 'string') {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return encodeRaw(data);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public set(key: Ino, data: Uint8Array): void {
|
|
39
|
-
try {
|
|
40
|
-
this.storage.setItem(key.toString(), decodeRaw(data));
|
|
41
|
-
} catch (e) {
|
|
42
|
-
throw new ErrnoError(Errno.ENOSPC, 'Storage is full.');
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
public delete(key: Ino): void {
|
|
47
|
-
try {
|
|
48
|
-
this.storage.removeItem(key.toString());
|
|
49
|
-
} catch (e) {
|
|
50
|
-
throw new ErrnoError(Errno.EIO, 'Unable to delete key ' + key + ': ' + e);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Options to pass to the StorageFileSystem
|
|
57
|
-
*/
|
|
58
|
-
export interface WebStorageOptions {
|
|
59
|
-
/**
|
|
60
|
-
* The Storage to use. Defaults to globalThis.localStorage.
|
|
61
|
-
*/
|
|
62
|
-
storage?: Storage;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* A synchronous file system backed by a `Storage` (e.g. localStorage).
|
|
67
|
-
*/
|
|
68
|
-
const _WebStorage = {
|
|
69
|
-
name: 'WebStorage',
|
|
70
|
-
|
|
71
|
-
options: {
|
|
72
|
-
storage: {
|
|
73
|
-
type: 'object',
|
|
74
|
-
required: false,
|
|
75
|
-
description: 'The Storage to use. Defaults to globalThis.localStorage.',
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
isAvailable(storage: Storage = globalThis.localStorage): boolean {
|
|
80
|
-
return storage instanceof globalThis.Storage;
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
create({ storage = globalThis.localStorage }: WebStorageOptions) {
|
|
84
|
-
return new StoreFS(new WebStorageStore(storage));
|
|
85
|
-
},
|
|
86
|
-
} as const satisfies Backend<StoreFS, WebStorageOptions>;
|
|
87
|
-
type _WebStorage = typeof _WebStorage;
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
89
|
-
interface WebStorage extends _WebStorage {}
|
|
90
|
-
export const WebStorage: WebStorage = _WebStorage;
|
package/src/access.ts
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
import type { Backend, FileSystemMetadata } from '@zenfs/core';
|
|
2
|
-
import { Async, Errno, ErrnoError, FileSystem, InMemory, PreloadFile, Stats } from '@zenfs/core';
|
|
3
|
-
import { S_IFDIR, S_IFREG } from '@zenfs/core/emulation/constants.js';
|
|
4
|
-
import { basename, dirname, join } from '@zenfs/core/emulation/path.js';
|
|
5
|
-
import { convertException, type ConvertException } from './utils.js';
|
|
6
|
-
|
|
7
|
-
declare global {
|
|
8
|
-
interface FileSystemDirectoryHandle {
|
|
9
|
-
[Symbol.iterator](): IterableIterator<[string, FileSystemHandle]>;
|
|
10
|
-
entries(): IterableIterator<[string, FileSystemHandle]>;
|
|
11
|
-
keys(): IterableIterator<string>;
|
|
12
|
-
values(): IterableIterator<FileSystemHandle>;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface WebAccessOptions {
|
|
17
|
-
handle: FileSystemDirectoryHandle;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export class WebAccessFS extends Async(FileSystem) {
|
|
21
|
-
private _handles: Map<string, FileSystemHandle> = new Map();
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @hidden
|
|
25
|
-
*/
|
|
26
|
-
_sync: FileSystem = InMemory.create({ name: 'accessfs-cache' });
|
|
27
|
-
|
|
28
|
-
public constructor(handle: FileSystemDirectoryHandle) {
|
|
29
|
-
super();
|
|
30
|
-
this._handles.set('/', handle);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public metadata(): FileSystemMetadata {
|
|
34
|
-
return {
|
|
35
|
-
...super.metadata(),
|
|
36
|
-
name: 'WebAccess',
|
|
37
|
-
noResizableBuffers: true,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public async sync(path: string, data: Uint8Array, stats: Stats): Promise<void> {
|
|
42
|
-
const currentStats = await this.stat(path);
|
|
43
|
-
if (stats.mtime !== currentStats.mtime) {
|
|
44
|
-
await this.writeFile(path, data);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
public async rename(oldPath: string, newPath: string): Promise<void> {
|
|
49
|
-
try {
|
|
50
|
-
const handle = await this.getHandle(oldPath);
|
|
51
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
52
|
-
const files = await this.readdir(oldPath);
|
|
53
|
-
|
|
54
|
-
await this.mkdir(newPath);
|
|
55
|
-
if (files.length == 0) {
|
|
56
|
-
await this.unlink(oldPath);
|
|
57
|
-
} else {
|
|
58
|
-
for (const file of files) {
|
|
59
|
-
await this.rename(join(oldPath, file), join(newPath, file));
|
|
60
|
-
await this.unlink(oldPath);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (!(handle instanceof FileSystemFileHandle)) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
const oldFile = await handle.getFile(),
|
|
68
|
-
destFolder = await this.getHandle(dirname(newPath));
|
|
69
|
-
if (!(destFolder instanceof FileSystemDirectoryHandle)) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
const newFile = await destFolder.getFileHandle(basename(newPath), { create: true });
|
|
73
|
-
const writable = await newFile.createWritable();
|
|
74
|
-
await writable.write(await oldFile.arrayBuffer());
|
|
75
|
-
|
|
76
|
-
await writable.close();
|
|
77
|
-
await this.unlink(oldPath);
|
|
78
|
-
} catch (ex) {
|
|
79
|
-
throw convertException(ex as ConvertException, oldPath, 'rename');
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public async writeFile(path: string, data: Uint8Array): Promise<void> {
|
|
84
|
-
if (data.buffer.resizable) {
|
|
85
|
-
throw new ErrnoError(Errno.EINVAL, 'Resizable buffers can not be written', path, 'write');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const handle = await this.getHandle(dirname(path));
|
|
89
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const file = await handle.getFileHandle(basename(path), { create: true });
|
|
94
|
-
const writable = await file.createWritable();
|
|
95
|
-
await writable.write(data);
|
|
96
|
-
await writable.close();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public async createFile(path: string, flag: string): Promise<PreloadFile<this>> {
|
|
100
|
-
await this.writeFile(path, new Uint8Array());
|
|
101
|
-
return this.openFile(path, flag);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
public async stat(path: string): Promise<Stats> {
|
|
105
|
-
const handle = await this.getHandle(path);
|
|
106
|
-
if (!handle) {
|
|
107
|
-
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
108
|
-
}
|
|
109
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
110
|
-
return new Stats({ mode: 0o777 | S_IFDIR, size: 4096 });
|
|
111
|
-
}
|
|
112
|
-
if (handle instanceof FileSystemFileHandle) {
|
|
113
|
-
const { lastModified, size } = await handle.getFile();
|
|
114
|
-
return new Stats({ mode: 0o777 | S_IFREG, size, mtimeMs: lastModified });
|
|
115
|
-
}
|
|
116
|
-
throw new ErrnoError(Errno.EBADE, 'Handle is not a directory or file', path, 'stat');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public async openFile(path: string, flag: string): Promise<PreloadFile<this>> {
|
|
120
|
-
const handle = await this.getHandle(path);
|
|
121
|
-
if (!(handle instanceof FileSystemFileHandle)) {
|
|
122
|
-
throw ErrnoError.With('EISDIR', path, 'openFile');
|
|
123
|
-
}
|
|
124
|
-
try {
|
|
125
|
-
const file = await handle.getFile();
|
|
126
|
-
const data = new Uint8Array(await file.arrayBuffer());
|
|
127
|
-
const stats = new Stats({ mode: 0o777 | S_IFREG, size: file.size, mtimeMs: file.lastModified });
|
|
128
|
-
return new PreloadFile(this, path, flag, stats, data);
|
|
129
|
-
} catch (ex) {
|
|
130
|
-
throw convertException(ex as ConvertException, path, 'openFile');
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public async unlink(path: string): Promise<void> {
|
|
135
|
-
const handle = await this.getHandle(dirname(path));
|
|
136
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
137
|
-
try {
|
|
138
|
-
await handle.removeEntry(basename(path), { recursive: true });
|
|
139
|
-
} catch (ex) {
|
|
140
|
-
throw convertException(ex as ConvertException, path, 'unlink');
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public async link(srcpath: string): Promise<void> {
|
|
146
|
-
throw ErrnoError.With('ENOSYS', srcpath, 'WebAccessFS.link');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public async rmdir(path: string): Promise<void> {
|
|
150
|
-
return this.unlink(path);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
public async mkdir(path: string): Promise<void> {
|
|
154
|
-
const existingHandle = await this.getHandle(path);
|
|
155
|
-
if (existingHandle) {
|
|
156
|
-
throw ErrnoError.With('EEXIST', path, 'mkdir');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const handle = await this.getHandle(dirname(path));
|
|
160
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
161
|
-
throw ErrnoError.With('ENOTDIR', path, 'mkdir');
|
|
162
|
-
}
|
|
163
|
-
await handle.getDirectoryHandle(basename(path), { create: true });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public async readdir(path: string): Promise<string[]> {
|
|
167
|
-
const handle = await this.getHandle(path);
|
|
168
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
169
|
-
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return Array.from(handle.keys());
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
protected async getHandle(path: string): Promise<FileSystemHandle> {
|
|
176
|
-
if (this._handles.has(path)) {
|
|
177
|
-
return this._handles.get(path)!;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
let walked = '/';
|
|
181
|
-
|
|
182
|
-
for (const part of path.split('/').slice(1)) {
|
|
183
|
-
const handle = this._handles.get(walked);
|
|
184
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
185
|
-
throw ErrnoError.With('ENOTDIR', walked, 'getHandle');
|
|
186
|
-
}
|
|
187
|
-
walked = join(walked, part);
|
|
188
|
-
|
|
189
|
-
try {
|
|
190
|
-
const dirHandle = await handle.getDirectoryHandle(part);
|
|
191
|
-
this._handles.set(walked, dirHandle);
|
|
192
|
-
} catch (_ex) {
|
|
193
|
-
const ex = _ex as DOMException;
|
|
194
|
-
if (ex.name == 'TypeMismatchError') {
|
|
195
|
-
try {
|
|
196
|
-
const fileHandle = await handle.getFileHandle(part);
|
|
197
|
-
this._handles.set(walked, fileHandle);
|
|
198
|
-
} catch (ex) {
|
|
199
|
-
convertException(ex as ConvertException, walked, 'getHandle');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (ex.name === 'TypeError') {
|
|
204
|
-
throw new ErrnoError(Errno.ENOENT, ex.message, walked, 'getHandle');
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
convertException(ex, walked, 'getHandle');
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return this._handles.get(path)!;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const _WebAccess = {
|
|
216
|
-
name: 'WebAccess',
|
|
217
|
-
|
|
218
|
-
options: {
|
|
219
|
-
handle: {
|
|
220
|
-
type: 'object',
|
|
221
|
-
required: true,
|
|
222
|
-
description: 'The directory handle to use for the root',
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
isAvailable(): boolean {
|
|
227
|
-
return typeof FileSystemHandle == 'function';
|
|
228
|
-
},
|
|
229
|
-
|
|
230
|
-
create(options: WebAccessOptions) {
|
|
231
|
-
return new WebAccessFS(options.handle);
|
|
232
|
-
},
|
|
233
|
-
} as const satisfies Backend<WebAccessFS, WebAccessOptions>;
|
|
234
|
-
type _WebAccess = typeof _WebAccess;
|
|
235
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
236
|
-
interface WebAccess extends _WebAccess {}
|
|
237
|
-
export const WebAccess: WebAccess = _WebAccess;
|
package/src/index.ts
DELETED
package/src/utils.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import { ErrnoError, Errno } from '@zenfs/core';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Converts a DOMException into an Errno
|
|
5
|
-
* @see https://developer.mozilla.org/Web/API/DOMException
|
|
6
|
-
*/
|
|
7
|
-
function errnoForDOMException(ex: DOMException): keyof typeof Errno {
|
|
8
|
-
switch (ex.name) {
|
|
9
|
-
case 'IndexSizeError':
|
|
10
|
-
case 'HierarchyRequestError':
|
|
11
|
-
case 'InvalidCharacterError':
|
|
12
|
-
case 'InvalidStateError':
|
|
13
|
-
case 'SyntaxError':
|
|
14
|
-
case 'NamespaceError':
|
|
15
|
-
case 'TypeMismatchError':
|
|
16
|
-
case 'ConstraintError':
|
|
17
|
-
case 'VersionError':
|
|
18
|
-
case 'URLMismatchError':
|
|
19
|
-
case 'InvalidNodeTypeError':
|
|
20
|
-
return 'EINVAL';
|
|
21
|
-
case 'WrongDocumentError':
|
|
22
|
-
return 'EXDEV';
|
|
23
|
-
case 'NoModificationAllowedError':
|
|
24
|
-
case 'InvalidModificationError':
|
|
25
|
-
case 'InvalidAccessError':
|
|
26
|
-
case 'SecurityError':
|
|
27
|
-
case 'NotAllowedError':
|
|
28
|
-
return 'EACCES';
|
|
29
|
-
case 'NotFoundError':
|
|
30
|
-
return 'ENOENT';
|
|
31
|
-
case 'NotSupportedError':
|
|
32
|
-
return 'ENOTSUP';
|
|
33
|
-
case 'InUseAttributeError':
|
|
34
|
-
return 'EBUSY';
|
|
35
|
-
case 'NetworkError':
|
|
36
|
-
return 'ENETDOWN';
|
|
37
|
-
case 'AbortError':
|
|
38
|
-
return 'EINTR';
|
|
39
|
-
case 'QuotaExceededError':
|
|
40
|
-
return 'ENOSPC';
|
|
41
|
-
case 'TimeoutError':
|
|
42
|
-
return 'ETIMEDOUT';
|
|
43
|
-
case 'ReadOnlyError':
|
|
44
|
-
return 'EROFS';
|
|
45
|
-
case 'DataCloneError':
|
|
46
|
-
case 'EncodingError':
|
|
47
|
-
case 'NotReadableError':
|
|
48
|
-
case 'DataError':
|
|
49
|
-
case 'TransactionInactiveError':
|
|
50
|
-
case 'OperationError':
|
|
51
|
-
case 'UnknownError':
|
|
52
|
-
default:
|
|
53
|
-
return 'EIO';
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** @internal */
|
|
58
|
-
export type ConvertException = ErrnoError | DOMException | Error;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Handles converting errors, then rethrowing them
|
|
62
|
-
* @internal
|
|
63
|
-
*/
|
|
64
|
-
export function convertException(ex: ConvertException, path?: string, syscall?: string): ErrnoError {
|
|
65
|
-
if (ex instanceof ErrnoError) {
|
|
66
|
-
return ex;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const code = ex instanceof DOMException ? Errno[errnoForDOMException(ex)] : Errno.EIO;
|
|
70
|
-
const error = new ErrnoError(code, ex.message, path, syscall);
|
|
71
|
-
error.stack = ex.stack!;
|
|
72
|
-
error.cause = ex.cause;
|
|
73
|
-
return error;
|
|
74
|
-
}
|