@zenfs/dom 1.1.2 → 1.1.4
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 +9 -9
- package/dist/IndexedDB.js +28 -37
- package/dist/access.d.ts +47 -13
- package/dist/access.js +190 -130
- package/dist/devices/dsp.js +6 -7
- package/dist/devices/framebuffer.d.ts +5 -1
- package/dist/devices/framebuffer.js +14 -14
- package/dist/storage.d.ts +8 -8
- package/dist/storage.js +3 -3
- package/dist/utils.js +1 -2
- package/dist/xml.d.ts +7 -6
- package/dist/xml.js +34 -25
- package/package.json +5 -4
- package/readme.md +4 -4
- package/tsconfig.json +3 -2
package/dist/IndexedDB.d.ts
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SharedConfig, Store } from '@zenfs/core';
|
|
2
2
|
import { AsyncTransaction, StoreFS } from '@zenfs/core';
|
|
3
|
+
import type * as cache from 'utilium/cache.js';
|
|
3
4
|
/**
|
|
4
|
-
* @hidden
|
|
5
|
+
* @internal @hidden
|
|
5
6
|
*/
|
|
6
7
|
export declare class IndexedDBTransaction extends AsyncTransaction<IndexedDBStore> {
|
|
7
8
|
tx: IDBTransaction;
|
|
8
9
|
store: IndexedDBStore;
|
|
9
10
|
private _idb;
|
|
10
11
|
constructor(tx: IDBTransaction, store: IndexedDBStore);
|
|
11
|
-
keys(): Promise<Iterable<
|
|
12
|
-
get(
|
|
13
|
-
set(
|
|
14
|
-
remove(
|
|
12
|
+
keys(): Promise<Iterable<number>>;
|
|
13
|
+
get(id: number): Promise<Uint8Array | undefined>;
|
|
14
|
+
set(id: number, data: Uint8Array): Promise<void>;
|
|
15
|
+
remove(id: number): Promise<void>;
|
|
15
16
|
commit(): Promise<void>;
|
|
16
17
|
abort(): Promise<void>;
|
|
17
18
|
}
|
|
18
19
|
export declare class IndexedDBStore implements Store {
|
|
19
20
|
protected db: IDBDatabase;
|
|
21
|
+
cache: Map<number, cache.Resource<number>>;
|
|
20
22
|
constructor(db: IDBDatabase);
|
|
21
23
|
sync(): Promise<void>;
|
|
22
24
|
get name(): string;
|
|
23
|
-
clear(): Promise<void>;
|
|
24
|
-
clearSync(): void;
|
|
25
25
|
transaction(): IndexedDBTransaction;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
@@ -53,7 +53,7 @@ declare const _IndexedDB: {
|
|
|
53
53
|
};
|
|
54
54
|
};
|
|
55
55
|
readonly isAvailable: (idbFactory?: IDBFactory) => Promise<boolean>;
|
|
56
|
-
readonly create: (options: IndexedDBOptions & Partial<SharedConfig>) => Promise<
|
|
56
|
+
readonly create: (options: IndexedDBOptions & Partial<SharedConfig>) => Promise<StoreFS<IndexedDBStore>>;
|
|
57
57
|
};
|
|
58
58
|
type _IndexedDB = typeof _IndexedDB;
|
|
59
59
|
export interface IndexedDB extends _IndexedDB {
|
package/dist/IndexedDB.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AsyncTransaction, StoreFS, log } from '@zenfs/core';
|
|
2
2
|
import { convertException } from './utils.js';
|
|
3
3
|
function wrap(request) {
|
|
4
4
|
return new Promise((resolve, reject) => {
|
|
@@ -10,7 +10,7 @@ function wrap(request) {
|
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* @hidden
|
|
13
|
+
* @internal @hidden
|
|
14
14
|
*/
|
|
15
15
|
export class IndexedDBTransaction extends AsyncTransaction {
|
|
16
16
|
constructor(tx, store) {
|
|
@@ -20,33 +20,30 @@ export class IndexedDBTransaction extends AsyncTransaction {
|
|
|
20
20
|
this._idb = tx.objectStore(store.name);
|
|
21
21
|
}
|
|
22
22
|
async keys() {
|
|
23
|
-
return (await wrap(this._idb.getAllKeys())).filter(k => typeof k == 'string').map(k =>
|
|
23
|
+
return (await wrap(this._idb.getAllKeys())).filter(k => typeof k == 'string').map(k => Number(k));
|
|
24
24
|
}
|
|
25
|
-
get(
|
|
26
|
-
|
|
25
|
+
async get(id) {
|
|
26
|
+
const data = await wrap(this._idb.get(id.toString()));
|
|
27
|
+
if (data)
|
|
28
|
+
this._cached(id, { size: data.byteLength }).add(data, 0);
|
|
29
|
+
return data;
|
|
27
30
|
}
|
|
28
|
-
async set(
|
|
29
|
-
|
|
31
|
+
async set(id, data) {
|
|
32
|
+
this._cached(id, { size: data.byteLength }).add(data, 0);
|
|
33
|
+
await wrap(this._idb.put(data, id.toString()));
|
|
30
34
|
}
|
|
31
|
-
remove(
|
|
32
|
-
|
|
35
|
+
remove(id) {
|
|
36
|
+
this.store.cache.delete(id);
|
|
37
|
+
return wrap(this._idb.delete(id.toString()));
|
|
33
38
|
}
|
|
34
39
|
async commit() {
|
|
35
|
-
if (this.done) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
40
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
39
|
-
this.done = true;
|
|
40
41
|
this.tx.oncomplete = () => resolve();
|
|
41
42
|
this.tx.onerror = () => reject(convertException(this.tx.error));
|
|
42
43
|
this.tx.commit();
|
|
43
44
|
return promise;
|
|
44
45
|
}
|
|
45
46
|
async abort() {
|
|
46
|
-
if (this.done) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
this.done = true;
|
|
50
47
|
const { promise, resolve, reject } = Promise.withResolvers();
|
|
51
48
|
this.tx.onabort = () => resolve();
|
|
52
49
|
this.tx.onerror = () => reject(convertException(this.tx.error));
|
|
@@ -60,29 +57,24 @@ async function createDB(name, indexedDB = globalThis.indexedDB) {
|
|
|
60
57
|
const db = req.result;
|
|
61
58
|
// This should never happen; we're at version 1. Why does another database exist?
|
|
62
59
|
if (db.objectStoreNames.contains(name)) {
|
|
60
|
+
log.warn('Found unexpected object store: ' + name);
|
|
63
61
|
db.deleteObjectStore(name);
|
|
64
62
|
}
|
|
65
63
|
db.createObjectStore(name);
|
|
66
64
|
};
|
|
67
|
-
|
|
68
|
-
return result;
|
|
65
|
+
return await wrap(req);
|
|
69
66
|
}
|
|
70
67
|
export class IndexedDBStore {
|
|
71
68
|
constructor(db) {
|
|
72
69
|
this.db = db;
|
|
70
|
+
this.cache = new Map();
|
|
73
71
|
}
|
|
74
72
|
sync() {
|
|
75
|
-
|
|
73
|
+
return Promise.resolve();
|
|
76
74
|
}
|
|
77
75
|
get name() {
|
|
78
76
|
return this.db.name;
|
|
79
77
|
}
|
|
80
|
-
clear() {
|
|
81
|
-
return wrap(this.db.transaction(this.name, 'readwrite').objectStore(this.name).clear());
|
|
82
|
-
}
|
|
83
|
-
clearSync() {
|
|
84
|
-
throw ErrnoError.With('ENOSYS', undefined, 'IndexedDBStore.clearSync');
|
|
85
|
-
}
|
|
86
78
|
transaction() {
|
|
87
79
|
const tx = this.db.transaction(this.name, 'readwrite');
|
|
88
80
|
return new IndexedDBTransaction(tx, this);
|
|
@@ -94,14 +86,8 @@ export class IndexedDBStore {
|
|
|
94
86
|
const _IndexedDB = {
|
|
95
87
|
name: 'IndexedDB',
|
|
96
88
|
options: {
|
|
97
|
-
storeName: {
|
|
98
|
-
|
|
99
|
-
required: false,
|
|
100
|
-
},
|
|
101
|
-
idbFactory: {
|
|
102
|
-
type: 'object',
|
|
103
|
-
required: false,
|
|
104
|
-
},
|
|
89
|
+
storeName: { type: 'string', required: false },
|
|
90
|
+
idbFactory: { type: 'object', required: false },
|
|
105
91
|
},
|
|
106
92
|
async isAvailable(idbFactory = globalThis.indexedDB) {
|
|
107
93
|
try {
|
|
@@ -122,9 +108,14 @@ const _IndexedDB = {
|
|
|
122
108
|
async create(options) {
|
|
123
109
|
const db = await createDB(options.storeName || 'zenfs', options.idbFactory);
|
|
124
110
|
const store = new IndexedDBStore(db);
|
|
125
|
-
const fs = new
|
|
126
|
-
if (
|
|
127
|
-
|
|
111
|
+
const fs = new StoreFS(store);
|
|
112
|
+
if (options?.disableAsyncCache) {
|
|
113
|
+
log.notice('Async preloading disabled for IndexedDB');
|
|
114
|
+
return fs;
|
|
115
|
+
}
|
|
116
|
+
const tx = store.transaction();
|
|
117
|
+
for (const id of await tx.keys()) {
|
|
118
|
+
await tx.get(id); // Adds to cache
|
|
128
119
|
}
|
|
129
120
|
return fs;
|
|
130
121
|
},
|
package/dist/access.d.ts
CHANGED
|
@@ -1,29 +1,60 @@
|
|
|
1
|
-
import type { CreationOptions,
|
|
2
|
-
import { FileSystem,
|
|
1
|
+
import type { CreationOptions, File, InodeLike, Stats } from '@zenfs/core';
|
|
2
|
+
import { FileSystem, Index } from '@zenfs/core';
|
|
3
3
|
export interface WebAccessOptions {
|
|
4
4
|
handle: FileSystemDirectoryHandle;
|
|
5
|
+
metadata?: string;
|
|
5
6
|
}
|
|
7
|
+
type HKindToType<T extends FileSystemHandleKind> = T extends 'directory' ? FileSystemDirectoryHandle : T extends 'file' ? FileSystemFileHandle : FileSystemHandle;
|
|
6
8
|
declare const WebAccessFS_base: import("@zenfs/core").Mixin<typeof FileSystem, import("@zenfs/core").AsyncMixin>;
|
|
7
9
|
export declare class WebAccessFS extends WebAccessFS_base {
|
|
8
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Disables index optimizations,
|
|
12
|
+
* like using the index for `readdir`
|
|
13
|
+
*/
|
|
14
|
+
private readonly disableIndexOptimizations;
|
|
15
|
+
protected index: Index;
|
|
16
|
+
protected _handles: Map<string, FileSystemHandle>;
|
|
17
|
+
/**
|
|
18
|
+
* Loads all of the handles.
|
|
19
|
+
* @internal @hidden
|
|
20
|
+
*/
|
|
21
|
+
_loadHandles(path: string, handle: FileSystemDirectoryHandle): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Loads metadata
|
|
24
|
+
* @internal @hidden
|
|
25
|
+
*/
|
|
26
|
+
_loadMetadata(metadataPath?: string): Promise<void>;
|
|
9
27
|
/**
|
|
10
28
|
* @hidden
|
|
11
29
|
*/
|
|
12
30
|
_sync: FileSystem;
|
|
13
|
-
constructor(handle: FileSystemDirectoryHandle
|
|
14
|
-
|
|
15
|
-
|
|
31
|
+
constructor(handle: FileSystemDirectoryHandle,
|
|
32
|
+
/**
|
|
33
|
+
* Disables index optimizations,
|
|
34
|
+
* like using the index for `readdir`
|
|
35
|
+
*/
|
|
36
|
+
disableIndexOptimizations?: boolean);
|
|
16
37
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
38
|
+
unlink(path: string): Promise<void>;
|
|
39
|
+
read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
|
|
40
|
+
write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Do not use!
|
|
43
|
+
* @deprecated @internal @hidden
|
|
44
|
+
*/
|
|
17
45
|
writeFile(path: string, data: Uint8Array): Promise<void>;
|
|
18
|
-
createFile(path: string, flag: string): Promise<PreloadFile<this>>;
|
|
19
46
|
stat(path: string): Promise<Stats>;
|
|
20
|
-
|
|
21
|
-
|
|
47
|
+
createFile(path: string, flag: string, mode: number, options: CreationOptions): Promise<File>;
|
|
48
|
+
openFile(path: string, flag: string): Promise<File>;
|
|
49
|
+
/**
|
|
50
|
+
* @todo Implement
|
|
51
|
+
*/
|
|
22
52
|
link(srcpath: string): Promise<void>;
|
|
53
|
+
sync(path: string, data?: Uint8Array, stats?: Readonly<Partial<InodeLike>>): Promise<void>;
|
|
23
54
|
rmdir(path: string): Promise<void>;
|
|
24
|
-
mkdir(path: string, mode
|
|
55
|
+
mkdir(path: string, mode: number, options: CreationOptions): Promise<void>;
|
|
25
56
|
readdir(path: string): Promise<string[]>;
|
|
26
|
-
protected
|
|
57
|
+
protected get<const T extends FileSystemHandleKind | null>(kind: T | undefined, path: string, syscall?: string): T extends FileSystemHandleKind ? HKindToType<T> : FileSystemHandle;
|
|
27
58
|
}
|
|
28
59
|
declare const _WebAccess: {
|
|
29
60
|
readonly name: "WebAccess";
|
|
@@ -32,9 +63,12 @@ declare const _WebAccess: {
|
|
|
32
63
|
readonly type: "object";
|
|
33
64
|
readonly required: true;
|
|
34
65
|
};
|
|
66
|
+
readonly metadata: {
|
|
67
|
+
readonly type: "string";
|
|
68
|
+
readonly required: false;
|
|
69
|
+
};
|
|
35
70
|
};
|
|
36
|
-
readonly
|
|
37
|
-
readonly create: (options: WebAccessOptions) => WebAccessFS;
|
|
71
|
+
readonly create: (options: WebAccessOptions) => Promise<WebAccessFS>;
|
|
38
72
|
};
|
|
39
73
|
type _WebAccess = typeof _WebAccess;
|
|
40
74
|
export interface WebAccess extends _WebAccess {
|
package/dist/access.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Async, Errno, ErrnoError, FileSystem, InMemory,
|
|
2
|
-
import { S_IFDIR, S_IFREG } from '@zenfs/core/
|
|
3
|
-
import { basename, dirname, join } from '@zenfs/core/
|
|
1
|
+
import { Async, constants, Errno, ErrnoError, FileSystem, Index, InMemory, Inode, LazyFile } from '@zenfs/core';
|
|
2
|
+
import { S_IFDIR, S_IFREG } from '@zenfs/core/vfs/constants.js';
|
|
3
|
+
import { basename, dirname, join } from '@zenfs/core/vfs/path.js';
|
|
4
|
+
import { _throw } from 'utilium';
|
|
4
5
|
import { convertException } from './utils.js';
|
|
5
6
|
function isResizable(buffer) {
|
|
6
7
|
if (buffer instanceof ArrayBuffer)
|
|
@@ -9,184 +10,243 @@ function isResizable(buffer) {
|
|
|
9
10
|
return buffer.growable;
|
|
10
11
|
return false;
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Since `FileSystemHandle.kind` doesn't have correct type support
|
|
15
|
+
*/
|
|
16
|
+
function isKind(handle, kind) {
|
|
17
|
+
return handle.kind == kind;
|
|
18
|
+
}
|
|
12
19
|
export class WebAccessFS extends Async(FileSystem) {
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Loads all of the handles.
|
|
22
|
+
* @internal @hidden
|
|
23
|
+
*/
|
|
24
|
+
async _loadHandles(path, handle) {
|
|
25
|
+
for await (const [key, child] of handle.entries()) {
|
|
26
|
+
const p = join(path, key);
|
|
27
|
+
this._handles.set(p, child);
|
|
28
|
+
if (isKind(child, 'directory'))
|
|
29
|
+
await this._loadHandles(p, child);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Loads metadata
|
|
34
|
+
* @internal @hidden
|
|
35
|
+
*/
|
|
36
|
+
async _loadMetadata(metadataPath) {
|
|
37
|
+
if (metadataPath) {
|
|
38
|
+
const handle = this.get('file', metadataPath);
|
|
39
|
+
const file = await handle.getFile();
|
|
40
|
+
const raw = await file.text();
|
|
41
|
+
const data = JSON.parse(raw);
|
|
42
|
+
this.index.fromJSON(data);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
for (const [path, handle] of this._handles) {
|
|
46
|
+
if (isKind(handle, 'file')) {
|
|
47
|
+
const { lastModified, size } = await handle.getFile();
|
|
48
|
+
this.index.set(path, new Inode({ mode: 0o644 | constants.S_IFREG, size, mtimeMs: lastModified }));
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!isKind(handle, 'directory'))
|
|
52
|
+
throw new ErrnoError(Errno.EIO, 'Invalid handle', path);
|
|
53
|
+
this.index.set(path, new Inode({ mode: 0o777 | constants.S_IFDIR, size: 0 }));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
constructor(handle,
|
|
57
|
+
/**
|
|
58
|
+
* Disables index optimizations,
|
|
59
|
+
* like using the index for `readdir`
|
|
60
|
+
*/
|
|
61
|
+
disableIndexOptimizations = false) {
|
|
62
|
+
super(0x77656261, 'webaccessfs');
|
|
63
|
+
this.disableIndexOptimizations = disableIndexOptimizations;
|
|
64
|
+
this.index = new Index();
|
|
15
65
|
this._handles = new Map();
|
|
16
66
|
/**
|
|
17
67
|
* @hidden
|
|
18
68
|
*/
|
|
19
69
|
this._sync = InMemory.create({ name: 'accessfs-cache' });
|
|
70
|
+
this.attributes.set('no_buffer_resize');
|
|
71
|
+
this.attributes.set('setid');
|
|
20
72
|
this._handles.set('/', handle);
|
|
21
73
|
}
|
|
22
|
-
metadata() {
|
|
23
|
-
return {
|
|
24
|
-
...super.metadata(),
|
|
25
|
-
name: 'WebAccess',
|
|
26
|
-
noResizableBuffers: true,
|
|
27
|
-
// Not really, but we don't support opening directories so this prevent the VFS from trying
|
|
28
|
-
features: ['setid'],
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
async sync(path, data) {
|
|
32
|
-
await this.writeFile(path, data);
|
|
33
|
-
}
|
|
34
74
|
async rename(oldPath, newPath) {
|
|
35
|
-
|
|
36
|
-
|
|
75
|
+
if (oldPath == newPath)
|
|
76
|
+
return;
|
|
77
|
+
if (newPath.startsWith(oldPath + '/'))
|
|
78
|
+
throw ErrnoError.With('EBUSY', oldPath, 'rename');
|
|
79
|
+
const handle = this.get(null, oldPath, 'rename');
|
|
80
|
+
if (isKind(handle, 'directory')) {
|
|
37
81
|
const files = await this.readdir(oldPath);
|
|
38
|
-
await this.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
for (const file of files) {
|
|
82
|
+
const stats = await this.stat(oldPath);
|
|
83
|
+
await this.mkdir(newPath, stats.mode, stats);
|
|
84
|
+
for (const file of files)
|
|
44
85
|
await this.rename(join(oldPath, file), join(newPath, file));
|
|
45
|
-
|
|
46
|
-
}
|
|
86
|
+
await this.rmdir(oldPath);
|
|
47
87
|
return;
|
|
48
88
|
}
|
|
49
|
-
if (!(handle
|
|
89
|
+
if (!isKind(handle, 'file')) {
|
|
50
90
|
throw new ErrnoError(Errno.ENOTSUP, 'Not a file or directory handle', oldPath, 'rename');
|
|
51
91
|
}
|
|
52
|
-
const oldFile = await handle.getFile().catch(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
const newFile = await destFolder.getFileHandle(basename(newPath), { create: true }).catch((ex) => {
|
|
59
|
-
throw convertException(ex, newPath, 'rename');
|
|
60
|
-
});
|
|
92
|
+
const oldFile = await handle.getFile().catch(ex => _throw(convertException(ex, oldPath, 'rename')));
|
|
93
|
+
const destFolder = this.get('directory', dirname(newPath), 'rename');
|
|
94
|
+
const newFile = await destFolder
|
|
95
|
+
.getFileHandle(basename(newPath), { create: true })
|
|
96
|
+
.catch((ex) => _throw(ex.name == 'TypeMismatchError' ? ErrnoError.With('EISDIR', newPath, 'rename') : convertException(ex, newPath, 'rename')));
|
|
61
97
|
const writable = await newFile.createWritable();
|
|
62
98
|
await writable.write(await oldFile.arrayBuffer());
|
|
63
99
|
await writable.close();
|
|
64
100
|
await this.unlink(oldPath);
|
|
101
|
+
this._handles.set(newPath, newFile);
|
|
65
102
|
}
|
|
66
|
-
async
|
|
67
|
-
if (
|
|
68
|
-
throw
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
103
|
+
async unlink(path) {
|
|
104
|
+
if (path == '/')
|
|
105
|
+
throw ErrnoError.With('EBUSY', '/', 'unlink');
|
|
106
|
+
const handle = this.get('directory', dirname(path), 'unlink');
|
|
107
|
+
await handle.removeEntry(basename(path), { recursive: true }).catch(ex => _throw(convertException(ex, path, 'unlink')));
|
|
108
|
+
this.index.delete(path);
|
|
109
|
+
}
|
|
110
|
+
async read(path, buffer, offset, end) {
|
|
111
|
+
if (end <= offset)
|
|
72
112
|
return;
|
|
73
|
-
|
|
74
|
-
const file = await handle.
|
|
75
|
-
const
|
|
76
|
-
|
|
113
|
+
const handle = this.get('file', path, 'write');
|
|
114
|
+
const file = await handle.getFile();
|
|
115
|
+
const data = await file.arrayBuffer();
|
|
116
|
+
if (data.byteLength < end - offset)
|
|
117
|
+
throw ErrnoError.With('ENODATA', path, 'read');
|
|
118
|
+
buffer.set(new Uint8Array(data, offset, end - offset));
|
|
119
|
+
}
|
|
120
|
+
async write(path, buffer, offset) {
|
|
121
|
+
if (isResizable(buffer.buffer)) {
|
|
122
|
+
const newBuffer = new Uint8Array(new ArrayBuffer(buffer.byteLength), buffer.byteOffset, buffer.byteLength);
|
|
123
|
+
newBuffer.set(buffer);
|
|
124
|
+
buffer = newBuffer;
|
|
125
|
+
}
|
|
126
|
+
const inode = this.index.get(path);
|
|
127
|
+
if (!inode)
|
|
128
|
+
throw ErrnoError.With('ENOENT', path, 'write');
|
|
129
|
+
const handle = this.get('file', path, 'write');
|
|
130
|
+
const writable = await handle.createWritable();
|
|
131
|
+
try {
|
|
132
|
+
await writable.seek(offset);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
await writable.write({ type: 'seek', position: offset });
|
|
136
|
+
}
|
|
137
|
+
await writable.write(buffer);
|
|
77
138
|
await writable.close();
|
|
139
|
+
const { size, lastModified } = await handle.getFile();
|
|
140
|
+
inode.update({ size, mtimeMs: lastModified });
|
|
141
|
+
this.index.set(path, inode);
|
|
78
142
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Do not use!
|
|
145
|
+
* @deprecated @internal @hidden
|
|
146
|
+
*/
|
|
147
|
+
async writeFile(path, data) {
|
|
148
|
+
return this.write(path, data, 0);
|
|
82
149
|
}
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
83
151
|
async stat(path) {
|
|
84
|
-
const
|
|
85
|
-
if (!
|
|
152
|
+
const inode = this.index.get(path);
|
|
153
|
+
if (!inode)
|
|
86
154
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
87
|
-
|
|
88
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
89
|
-
return new Stats({ mode: 0o777 | S_IFDIR, size: 4096 });
|
|
90
|
-
}
|
|
91
|
-
if (handle instanceof FileSystemFileHandle) {
|
|
92
|
-
const { lastModified, size } = await handle.getFile();
|
|
93
|
-
return new Stats({ mode: 0o777 | S_IFREG, size, mtimeMs: lastModified });
|
|
94
|
-
}
|
|
95
|
-
throw new ErrnoError(Errno.EBADE, 'Handle is not a directory or file', path, 'stat');
|
|
155
|
+
return inode.toStats();
|
|
96
156
|
}
|
|
97
|
-
async
|
|
98
|
-
const handle =
|
|
99
|
-
if (
|
|
100
|
-
throw ErrnoError.With('
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return new PreloadFile(this, path, flag, stats, data);
|
|
157
|
+
async createFile(path, flag, mode, options) {
|
|
158
|
+
const handle = this.get('directory', dirname(path), 'createFile');
|
|
159
|
+
if (this.index.has(path))
|
|
160
|
+
throw ErrnoError.With('EEXIST', path, 'createFile');
|
|
161
|
+
const file = await handle.getFileHandle(basename(path), { create: true });
|
|
162
|
+
// Race condition bypass
|
|
163
|
+
const inode = this.index.get(path) ?? new Inode({ ...options, mode: mode | S_IFREG });
|
|
164
|
+
this.index.set(path, inode);
|
|
165
|
+
this._handles.set(path, file);
|
|
166
|
+
return new LazyFile(this, path, flag, inode);
|
|
108
167
|
}
|
|
109
|
-
async
|
|
110
|
-
const
|
|
111
|
-
if (!
|
|
112
|
-
throw ErrnoError.With('
|
|
113
|
-
|
|
114
|
-
await handle.removeEntry(basename(path), { recursive: true }).catch((ex) => {
|
|
115
|
-
throw convertException(ex, path, 'unlink');
|
|
116
|
-
});
|
|
168
|
+
async openFile(path, flag) {
|
|
169
|
+
const inode = this.index.get(path);
|
|
170
|
+
if (!inode)
|
|
171
|
+
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
172
|
+
return new LazyFile(this, path, flag, inode.toStats());
|
|
117
173
|
}
|
|
118
|
-
|
|
174
|
+
/**
|
|
175
|
+
* @todo Implement
|
|
176
|
+
*/
|
|
119
177
|
async link(srcpath) {
|
|
120
178
|
return;
|
|
121
179
|
}
|
|
180
|
+
async sync(path, data, stats) {
|
|
181
|
+
const inode = this.index.get(path) ?? new Inode();
|
|
182
|
+
inode.update(stats);
|
|
183
|
+
this.index.set(path, inode);
|
|
184
|
+
if (!data)
|
|
185
|
+
return;
|
|
186
|
+
const handle = this.get('file', path, 'write');
|
|
187
|
+
const writable = await handle.createWritable();
|
|
188
|
+
try {
|
|
189
|
+
await writable.seek(0);
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
await writable.write({ type: 'seek', position: 0 });
|
|
193
|
+
}
|
|
194
|
+
await writable.write(data);
|
|
195
|
+
await writable.close();
|
|
196
|
+
}
|
|
122
197
|
async rmdir(path) {
|
|
123
|
-
|
|
198
|
+
if ((await this.readdir(path)).length)
|
|
199
|
+
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
200
|
+
if (path == '/')
|
|
201
|
+
throw ErrnoError.With('EBUSY', '/', 'rmdir');
|
|
202
|
+
const handle = this.get('directory', dirname(path), 'rmdir');
|
|
203
|
+
await handle.removeEntry(basename(path), { recursive: true }).catch(ex => _throw(convertException(ex, path, 'rmdir')));
|
|
204
|
+
this.index.delete(path);
|
|
124
205
|
}
|
|
125
206
|
async mkdir(path, mode, options) {
|
|
126
|
-
|
|
127
|
-
if (ex.code != 'ENOENT') {
|
|
128
|
-
throw ex;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
if (existingHandle) {
|
|
207
|
+
if (this.index.has(path))
|
|
132
208
|
throw ErrnoError.With('EEXIST', path, 'mkdir');
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
await handle.getDirectoryHandle(basename(path), { create: true });
|
|
209
|
+
const handle = this.get('directory', dirname(path), 'mkdir');
|
|
210
|
+
const dir = await handle.getDirectoryHandle(basename(path), { create: true });
|
|
211
|
+
this._handles.set(path, dir);
|
|
212
|
+
this.index.set(path, new Inode({ ...options, mode: mode | S_IFDIR }));
|
|
139
213
|
}
|
|
140
214
|
async readdir(path) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
215
|
+
if (!this.disableIndexOptimizations) {
|
|
216
|
+
if (!this.index.has(path))
|
|
217
|
+
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
218
|
+
const entries = this.index.directoryEntries(path);
|
|
219
|
+
if (!entries)
|
|
220
|
+
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
221
|
+
return Object.keys(entries);
|
|
222
|
+
}
|
|
223
|
+
const handle = this.get('directory', path, 'readdir');
|
|
145
224
|
const entries = [];
|
|
146
225
|
for await (const k of handle.keys()) {
|
|
147
226
|
entries.push(k);
|
|
148
227
|
}
|
|
149
228
|
return entries;
|
|
150
229
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
159
|
-
throw ErrnoError.With('ENOTDIR', walked, 'getHandle');
|
|
160
|
-
}
|
|
161
|
-
walked = join(walked, part);
|
|
162
|
-
const child = await handle.getDirectoryHandle(part).catch((ex) => {
|
|
163
|
-
switch (ex.name) {
|
|
164
|
-
case 'TypeMismatchError':
|
|
165
|
-
return handle.getFileHandle(part).catch((ex) => {
|
|
166
|
-
//throw convertException(ex, walked, 'getHandle');
|
|
167
|
-
});
|
|
168
|
-
case 'TypeError':
|
|
169
|
-
throw new ErrnoError(Errno.ENOENT, ex.message, walked, 'getHandle');
|
|
170
|
-
default:
|
|
171
|
-
throw convertException(ex, walked, 'getHandle');
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
if (child)
|
|
175
|
-
this._handles.set(walked, child);
|
|
176
|
-
}
|
|
177
|
-
return this._handles.get(path);
|
|
230
|
+
get(kind = null, path, syscall) {
|
|
231
|
+
const handle = this._handles.get(path);
|
|
232
|
+
if (!handle)
|
|
233
|
+
throw ErrnoError.With('ENODATA', path, syscall);
|
|
234
|
+
if (kind && !isKind(handle, kind))
|
|
235
|
+
throw ErrnoError.With(kind == 'directory' ? 'ENOTDIR' : 'EISDIR', path, syscall);
|
|
236
|
+
return handle;
|
|
178
237
|
}
|
|
179
238
|
}
|
|
180
239
|
const _WebAccess = {
|
|
181
240
|
name: 'WebAccess',
|
|
182
241
|
options: {
|
|
183
242
|
handle: { type: 'object', required: true },
|
|
243
|
+
metadata: { type: 'string', required: false },
|
|
184
244
|
},
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return
|
|
245
|
+
async create(options) {
|
|
246
|
+
const fs = new WebAccessFS(options.handle);
|
|
247
|
+
await fs._loadHandles('/', options.handle);
|
|
248
|
+
await fs._loadMetadata(options.metadata);
|
|
249
|
+
return fs;
|
|
190
250
|
},
|
|
191
251
|
};
|
|
192
252
|
export const WebAccess = _WebAccess;
|
package/dist/devices/dsp.js
CHANGED
|
@@ -8,8 +8,8 @@ if ('AudioWorkletProcessor' in globalThis) {
|
|
|
8
8
|
}
|
|
9
9
|
process(inputs, outputs) {
|
|
10
10
|
if (this.buffer && this.buffer.byteLength >= 128) {
|
|
11
|
-
outputs[0][0].set(this.buffer.
|
|
12
|
-
this.buffer = this.buffer.
|
|
11
|
+
outputs[0][0].set(this.buffer.subarray(0, 128));
|
|
12
|
+
this.buffer = this.buffer.subarray(128);
|
|
13
13
|
}
|
|
14
14
|
return true;
|
|
15
15
|
}
|
|
@@ -44,12 +44,11 @@ export async function dsp(options = {}) {
|
|
|
44
44
|
init(ino, options) {
|
|
45
45
|
return { data: dsp, major: 14, minor: 3 };
|
|
46
46
|
},
|
|
47
|
-
|
|
48
|
-
return
|
|
47
|
+
readD() {
|
|
48
|
+
return;
|
|
49
49
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return data.byteLength;
|
|
50
|
+
writeD(device, buffer, offset) {
|
|
51
|
+
device.data.port.postMessage(buffer.buffer);
|
|
53
52
|
},
|
|
54
53
|
};
|
|
55
54
|
}
|
|
@@ -2,6 +2,10 @@ import type { DeviceDriver } from '@zenfs/core';
|
|
|
2
2
|
export interface FramebufferOptions {
|
|
3
3
|
canvas?: HTMLCanvasElement | null;
|
|
4
4
|
}
|
|
5
|
+
export interface FramebufferData {
|
|
6
|
+
context: CanvasRenderingContext2D;
|
|
7
|
+
image: ImageData;
|
|
8
|
+
}
|
|
5
9
|
/**
|
|
6
10
|
* A frame buffer
|
|
7
11
|
*
|
|
@@ -10,4 +14,4 @@ export interface FramebufferOptions {
|
|
|
10
14
|
* addDevice(framebuffer, { canvas: document.querySelector('#your-canvas') })
|
|
11
15
|
* ```
|
|
12
16
|
*/
|
|
13
|
-
export declare const framebuffer: DeviceDriver<
|
|
17
|
+
export declare const framebuffer: DeviceDriver<FramebufferData>;
|
|
@@ -15,22 +15,22 @@ export const framebuffer = {
|
|
|
15
15
|
canvas = document.createElement('canvas');
|
|
16
16
|
document.body.appendChild(canvas);
|
|
17
17
|
}
|
|
18
|
-
const
|
|
19
|
-
if (!
|
|
18
|
+
const context = canvas.getContext('2d');
|
|
19
|
+
if (!context) {
|
|
20
20
|
throw new ErrnoError(Errno.EIO, 'Could not get context from canvas whilst initializing frame buffer.');
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
const image = new ImageData(canvas.width, canvas.height);
|
|
23
|
+
return {
|
|
24
|
+
data: { context, image },
|
|
25
|
+
major: 29,
|
|
26
|
+
minor: framebufferN++,
|
|
27
|
+
name: 'fb',
|
|
28
|
+
};
|
|
23
29
|
},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (data.byteLength < 4 * width * height) {
|
|
30
|
-
return 0;
|
|
31
|
-
}
|
|
32
|
-
const imageData = new ImageData(new Uint8ClampedArray(data), width, height);
|
|
33
|
-
file.device.data.putImageData(imageData, 0, 0);
|
|
34
|
-
return data.byteLength;
|
|
30
|
+
readD() { },
|
|
31
|
+
writeD({ data: { image, context } }, buffer, offset) {
|
|
32
|
+
image.data.set(buffer, offset);
|
|
33
|
+
context.putImageData(image, 0, 0);
|
|
34
|
+
return buffer.byteLength;
|
|
35
35
|
},
|
|
36
36
|
};
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { SyncMapStore, Store } from '@zenfs/core';
|
|
2
|
+
import { StoreFS, SyncMapTransaction } from '@zenfs/core';
|
|
3
3
|
/**
|
|
4
4
|
* A synchronous key-value store backed by Storage.
|
|
5
5
|
*/
|
|
6
|
-
export declare class WebStorageStore implements Store,
|
|
6
|
+
export declare class WebStorageStore implements Store, SyncMapStore {
|
|
7
7
|
protected storage: Storage;
|
|
8
8
|
get name(): string;
|
|
9
9
|
constructor(storage: Storage);
|
|
10
10
|
clear(): void;
|
|
11
11
|
clearSync(): void;
|
|
12
12
|
sync(): Promise<void>;
|
|
13
|
-
transaction():
|
|
14
|
-
keys(): Iterable<
|
|
15
|
-
get(key:
|
|
16
|
-
set(key:
|
|
17
|
-
delete(key:
|
|
13
|
+
transaction(): SyncMapTransaction;
|
|
14
|
+
keys(): Iterable<number>;
|
|
15
|
+
get(key: number): Uint8Array | undefined;
|
|
16
|
+
set(key: number, data: Uint8Array): void;
|
|
17
|
+
delete(key: number): void;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
20
|
* Options to pass to the StorageFileSystem
|
package/dist/storage.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrnoError, Errno,
|
|
1
|
+
import { ErrnoError, Errno, StoreFS, decodeRaw, encodeRaw, SyncMapTransaction } from '@zenfs/core';
|
|
2
2
|
/**
|
|
3
3
|
* A synchronous key-value store backed by Storage.
|
|
4
4
|
*/
|
|
@@ -18,10 +18,10 @@ export class WebStorageStore {
|
|
|
18
18
|
}
|
|
19
19
|
async sync() { }
|
|
20
20
|
transaction() {
|
|
21
|
-
return new
|
|
21
|
+
return new SyncMapTransaction(this);
|
|
22
22
|
}
|
|
23
23
|
keys() {
|
|
24
|
-
return Object.keys(this.storage).map(k =>
|
|
24
|
+
return Object.keys(this.storage).map(k => Number(k));
|
|
25
25
|
}
|
|
26
26
|
get(key) {
|
|
27
27
|
const data = this.storage.getItem(key.toString());
|
package/dist/utils.js
CHANGED
|
@@ -57,9 +57,8 @@ function errnoForDOMException(ex) {
|
|
|
57
57
|
* @internal
|
|
58
58
|
*/
|
|
59
59
|
export function convertException(ex, path, syscall) {
|
|
60
|
-
if (ex instanceof ErrnoError)
|
|
60
|
+
if (ex instanceof ErrnoError)
|
|
61
61
|
return ex;
|
|
62
|
-
}
|
|
63
62
|
const code = ex instanceof DOMException ? Errno[errnoForDOMException(ex)] : Errno.EIO;
|
|
64
63
|
const error = new ErrnoError(code, ex.message, path, syscall);
|
|
65
64
|
error.stack = ex.stack;
|
package/dist/xml.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CreationOptions, File,
|
|
1
|
+
import type { CreationOptions, File, InodeLike, StatsLike } from '@zenfs/core';
|
|
2
2
|
import { FileSystem, Stats } from '@zenfs/core';
|
|
3
3
|
export interface XMLOptions {
|
|
4
4
|
/**
|
|
@@ -9,15 +9,14 @@ export interface XMLOptions {
|
|
|
9
9
|
declare const XMLFS_base: import("@zenfs/core").Mixin<typeof FileSystem, import("@zenfs/core").AsyncFSMethods>;
|
|
10
10
|
export declare class XMLFS extends XMLFS_base {
|
|
11
11
|
/**
|
|
12
|
-
* @
|
|
12
|
+
* @inheritDoc XMLOptions.root
|
|
13
13
|
*/
|
|
14
14
|
readonly root: Element;
|
|
15
15
|
constructor(
|
|
16
16
|
/**
|
|
17
|
-
* @
|
|
17
|
+
* @inheritDoc XMLOptions.root
|
|
18
18
|
*/
|
|
19
19
|
root?: Element);
|
|
20
|
-
metadata(): FileSystemMetadata;
|
|
21
20
|
renameSync(oldPath: string, newPath: string): void;
|
|
22
21
|
statSync(path: string): Stats;
|
|
23
22
|
openFileSync(path: string, flag: string): File;
|
|
@@ -27,10 +26,12 @@ export declare class XMLFS extends XMLFS_base {
|
|
|
27
26
|
mkdirSync(path: string, mode: number, { uid, gid }: CreationOptions): void;
|
|
28
27
|
readdirSync(path: string): string[];
|
|
29
28
|
linkSync(target: string, link: string): void;
|
|
30
|
-
syncSync(path: string, data
|
|
29
|
+
syncSync(path: string, data?: Uint8Array, stats?: Readonly<Partial<InodeLike>>): void;
|
|
30
|
+
readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
|
|
31
|
+
writeSync(path: string, buffer: Uint8Array, offset: number): void;
|
|
31
32
|
toString(): string;
|
|
32
33
|
protected get(syscall: string, path: string): Element;
|
|
33
|
-
protected create(syscall: string, path: string, stats: Partial<
|
|
34
|
+
protected create(syscall: string, path: string, stats: Partial<InodeLike> & Pick<StatsLike, 'mode'>): Element;
|
|
34
35
|
protected add(syscall: string, node: Element, path: string, contents?: boolean): void;
|
|
35
36
|
protected remove(syscall: string, node: Element, path: string, contents?: boolean): void;
|
|
36
37
|
}
|
package/dist/xml.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import { decodeRaw, encodeRaw, Errno, ErrnoError, FileSystem,
|
|
2
|
-
import {
|
|
3
|
-
import { basename, dirname } from '@zenfs/core/emulation/path.js';
|
|
4
|
-
const statsLikeKeys = ['size', 'mode', 'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs', 'uid', 'gid', 'ino', 'nlink'];
|
|
1
|
+
import { _inode_fields, constants, decodeRaw, encodeRaw, Errno, ErrnoError, FileSystem, LazyFile, Stats, Sync } from '@zenfs/core';
|
|
2
|
+
import { basename, dirname } from '@zenfs/core/vfs/path.js';
|
|
5
3
|
function get_stats(node) {
|
|
6
4
|
const stats = {};
|
|
7
|
-
for (const key of
|
|
5
|
+
for (const key of _inode_fields) {
|
|
8
6
|
const value = node.getAttribute(key);
|
|
9
|
-
|
|
7
|
+
if (value !== null && value !== undefined)
|
|
8
|
+
stats[key] = parseInt(value, 16);
|
|
10
9
|
}
|
|
11
10
|
return new Stats(stats);
|
|
12
11
|
}
|
|
13
12
|
function set_stats(node, stats) {
|
|
14
|
-
for (const key of
|
|
15
|
-
if (stats[key]
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
for (const key of Object.keys(stats)) {
|
|
14
|
+
if (!(key in _inode_fields) || stats[key] === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
node.setAttribute(key, stats[key].toString(16));
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
function get_paths(node, contents = false) {
|
|
@@ -31,11 +30,12 @@ function get_paths(node, contents = false) {
|
|
|
31
30
|
export class XMLFS extends Sync(FileSystem) {
|
|
32
31
|
constructor(
|
|
33
32
|
/**
|
|
34
|
-
* @
|
|
33
|
+
* @inheritDoc XMLOptions.root
|
|
35
34
|
*/
|
|
36
35
|
root = new DOMParser().parseFromString('<fs></fs>', 'application/xml').documentElement) {
|
|
37
|
-
super();
|
|
36
|
+
super(0x20786d6c, 'xmltmpfs');
|
|
38
37
|
this.root = root;
|
|
38
|
+
this.attributes.set('setid');
|
|
39
39
|
try {
|
|
40
40
|
this.mkdirSync('/', 0o777, { uid: 0, gid: 0 });
|
|
41
41
|
}
|
|
@@ -45,9 +45,6 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
45
45
|
throw error;
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
metadata() {
|
|
49
|
-
return { ...super.metadata(), features: ['setid'] };
|
|
50
|
-
}
|
|
51
48
|
renameSync(oldPath, newPath) {
|
|
52
49
|
const node = this.get('rename', oldPath);
|
|
53
50
|
this.remove('rename', node, oldPath);
|
|
@@ -58,17 +55,17 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
58
55
|
}
|
|
59
56
|
openFileSync(path, flag) {
|
|
60
57
|
const node = this.get('openFile', path);
|
|
61
|
-
return new
|
|
58
|
+
return new LazyFile(this, path, flag, get_stats(node));
|
|
62
59
|
}
|
|
63
60
|
createFileSync(path, flag, mode, { uid, gid }) {
|
|
64
61
|
const parent = this.statSync(dirname(path));
|
|
65
62
|
const stats = new Stats({
|
|
66
|
-
mode: mode | S_IFREG,
|
|
67
|
-
uid: parent.mode & S_ISUID ? parent.uid : uid,
|
|
68
|
-
gid: parent.mode & S_ISGID ? parent.gid : gid,
|
|
63
|
+
mode: mode | constants.S_IFREG,
|
|
64
|
+
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
|
|
65
|
+
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
|
|
69
66
|
});
|
|
70
67
|
this.create('createFile', path, stats);
|
|
71
|
-
return new
|
|
68
|
+
return new LazyFile(this, path, flag, stats);
|
|
72
69
|
}
|
|
73
70
|
unlinkSync(path) {
|
|
74
71
|
const node = this.get('unlink', path);
|
|
@@ -87,9 +84,9 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
87
84
|
mkdirSync(path, mode, { uid, gid }) {
|
|
88
85
|
const parent = this.statSync(dirname(path));
|
|
89
86
|
const node = this.create('mkdir', path, {
|
|
90
|
-
mode: mode | S_IFDIR,
|
|
91
|
-
uid: parent.mode & S_ISUID ? parent.uid : uid,
|
|
92
|
-
gid: parent.mode & S_ISGID ? parent.gid : gid,
|
|
87
|
+
mode: mode | constants.S_IFDIR,
|
|
88
|
+
uid: parent.mode & constants.S_ISUID ? parent.uid : uid,
|
|
89
|
+
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
|
|
93
90
|
});
|
|
94
91
|
node.textContent = '[]';
|
|
95
92
|
}
|
|
@@ -108,11 +105,23 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
108
105
|
const node = this.get('link', target);
|
|
109
106
|
this.add('link', node, link);
|
|
110
107
|
}
|
|
111
|
-
syncSync(path, data, stats) {
|
|
108
|
+
syncSync(path, data, stats = {}) {
|
|
112
109
|
const node = this.get('sync', path);
|
|
113
|
-
|
|
110
|
+
if (data)
|
|
111
|
+
node.textContent = decodeRaw(data);
|
|
114
112
|
set_stats(node, stats);
|
|
115
113
|
}
|
|
114
|
+
readSync(path, buffer, offset, end) {
|
|
115
|
+
const node = this.get('read', path);
|
|
116
|
+
const raw = encodeRaw(node.textContent.slice(offset, end));
|
|
117
|
+
buffer.set(raw);
|
|
118
|
+
}
|
|
119
|
+
writeSync(path, buffer, offset) {
|
|
120
|
+
const node = this.get('write', path);
|
|
121
|
+
const data = decodeRaw(buffer);
|
|
122
|
+
const after = node.textContent.slice(offset + data.length);
|
|
123
|
+
node.textContent = node.textContent.slice(0, offset) + data + after;
|
|
124
|
+
}
|
|
116
125
|
toString() {
|
|
117
126
|
return new XMLSerializer().serializeToString(this.root);
|
|
118
127
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/dom",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "DOM backends for ZenFS",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"lint": "eslint src",
|
|
46
46
|
"build": "tsc -p tsconfig.json",
|
|
47
47
|
"build:docs": "typedoc --out docs --name 'ZenFS DOM' src/index.ts",
|
|
48
|
-
"test": "
|
|
48
|
+
"test": "npx zenfs-test -abcf",
|
|
49
49
|
"prepublishOnly": "npm run build"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@eslint/js": "^9.12.0",
|
|
53
|
+
"c8": "^10.1.3",
|
|
53
54
|
"eslint": "^9.12.0",
|
|
54
55
|
"fake-indexeddb": "^6.0.0",
|
|
55
|
-
"file-system-access": "^1.0.4",
|
|
56
56
|
"globals": "^15.10.0",
|
|
57
57
|
"prettier": "^3.2.5",
|
|
58
58
|
"tsx": "^4.19.2",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"typescript-eslint": "^8.8.1"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@zenfs/core": "^1.
|
|
64
|
+
"@zenfs/core": "^1.9.0",
|
|
65
|
+
"utilium": "^1.2.10"
|
|
65
66
|
},
|
|
66
67
|
"keywords": [
|
|
67
68
|
"filesystem",
|
package/readme.md
CHANGED
|
@@ -6,10 +6,10 @@ Please read the ZenFS core documentation!
|
|
|
6
6
|
|
|
7
7
|
## Backends
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
9
|
+
- `WebStorage` stores files in a `Storage` object, like `localStorage` and `sessionStorage`.
|
|
10
|
+
- `IndexedDB` stores files into an `IndexedDB` object database.
|
|
11
|
+
- `WebAccess` uses the [File System Access API](https://developer.mozilla.org/Web/API/File_System_API).
|
|
12
|
+
- `XML` uses an `XMLDocument` to store files, which can be appended to the DOM.
|
|
13
13
|
|
|
14
14
|
For more information, see the [API documentation](https://zen-fs.github.io/dom).
|
|
15
15
|
|
package/tsconfig.json
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
"module": "NodeNext",
|
|
4
4
|
"target": "ES2020",
|
|
5
5
|
"outDir": "dist",
|
|
6
|
-
"lib": ["ESNext", "DOM"],
|
|
6
|
+
"lib": ["ESNext", "DOM", "ESNext.AsyncIterable", "DOM.AsyncIterable"],
|
|
7
7
|
"strict": true,
|
|
8
8
|
"moduleResolution": "NodeNext",
|
|
9
|
-
"declaration": true
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"verbatimModuleSyntax": true
|
|
10
11
|
},
|
|
11
12
|
"include": ["src/**/*"],
|
|
12
13
|
"exclude": ["node_modules"]
|