@zenfs/dom 1.1.3 → 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 +189 -128
- 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 +27 -17
- 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,5 +1,7 @@
|
|
|
1
|
-
import { Async, constants, Errno, ErrnoError, FileSystem, InMemory,
|
|
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';
|
|
2
3
|
import { basename, dirname, join } from '@zenfs/core/vfs/path.js';
|
|
4
|
+
import { _throw } from 'utilium';
|
|
3
5
|
import { convertException } from './utils.js';
|
|
4
6
|
function isResizable(buffer) {
|
|
5
7
|
if (buffer instanceof ArrayBuffer)
|
|
@@ -8,184 +10,243 @@ function isResizable(buffer) {
|
|
|
8
10
|
return buffer.growable;
|
|
9
11
|
return false;
|
|
10
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Since `FileSystemHandle.kind` doesn't have correct type support
|
|
15
|
+
*/
|
|
16
|
+
function isKind(handle, kind) {
|
|
17
|
+
return handle.kind == kind;
|
|
18
|
+
}
|
|
11
19
|
export class WebAccessFS extends Async(FileSystem) {
|
|
12
|
-
|
|
13
|
-
|
|
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();
|
|
14
65
|
this._handles = new Map();
|
|
15
66
|
/**
|
|
16
67
|
* @hidden
|
|
17
68
|
*/
|
|
18
69
|
this._sync = InMemory.create({ name: 'accessfs-cache' });
|
|
70
|
+
this.attributes.set('no_buffer_resize');
|
|
71
|
+
this.attributes.set('setid');
|
|
19
72
|
this._handles.set('/', handle);
|
|
20
73
|
}
|
|
21
|
-
metadata() {
|
|
22
|
-
return {
|
|
23
|
-
...super.metadata(),
|
|
24
|
-
name: 'WebAccess',
|
|
25
|
-
noResizableBuffers: true,
|
|
26
|
-
// Not really, but we don't support opening directories so this prevent the VFS from trying
|
|
27
|
-
features: ['setid'],
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
async sync(path, data) {
|
|
31
|
-
await this.writeFile(path, data);
|
|
32
|
-
}
|
|
33
74
|
async rename(oldPath, newPath) {
|
|
34
|
-
|
|
35
|
-
|
|
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')) {
|
|
36
81
|
const files = await this.readdir(oldPath);
|
|
37
|
-
await this.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
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)
|
|
43
85
|
await this.rename(join(oldPath, file), join(newPath, file));
|
|
44
|
-
|
|
45
|
-
}
|
|
86
|
+
await this.rmdir(oldPath);
|
|
46
87
|
return;
|
|
47
88
|
}
|
|
48
|
-
if (!(handle
|
|
89
|
+
if (!isKind(handle, 'file')) {
|
|
49
90
|
throw new ErrnoError(Errno.ENOTSUP, 'Not a file or directory handle', oldPath, 'rename');
|
|
50
91
|
}
|
|
51
|
-
const oldFile = await handle.getFile().catch(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
const newFile = await destFolder.getFileHandle(basename(newPath), { create: true }).catch((ex) => {
|
|
58
|
-
throw convertException(ex, newPath, 'rename');
|
|
59
|
-
});
|
|
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')));
|
|
60
97
|
const writable = await newFile.createWritable();
|
|
61
98
|
await writable.write(await oldFile.arrayBuffer());
|
|
62
99
|
await writable.close();
|
|
63
100
|
await this.unlink(oldPath);
|
|
101
|
+
this._handles.set(newPath, newFile);
|
|
64
102
|
}
|
|
65
|
-
async
|
|
66
|
-
if (
|
|
67
|
-
throw
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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)
|
|
71
112
|
return;
|
|
72
|
-
|
|
73
|
-
const file = await handle.
|
|
74
|
-
const
|
|
75
|
-
|
|
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);
|
|
76
138
|
await writable.close();
|
|
139
|
+
const { size, lastModified } = await handle.getFile();
|
|
140
|
+
inode.update({ size, mtimeMs: lastModified });
|
|
141
|
+
this.index.set(path, inode);
|
|
77
142
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Do not use!
|
|
145
|
+
* @deprecated @internal @hidden
|
|
146
|
+
*/
|
|
147
|
+
async writeFile(path, data) {
|
|
148
|
+
return this.write(path, data, 0);
|
|
81
149
|
}
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
82
151
|
async stat(path) {
|
|
83
|
-
const
|
|
84
|
-
if (!
|
|
152
|
+
const inode = this.index.get(path);
|
|
153
|
+
if (!inode)
|
|
85
154
|
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
86
|
-
|
|
87
|
-
if (handle instanceof FileSystemDirectoryHandle) {
|
|
88
|
-
return new Stats({ mode: 0o777 | constants.S_IFDIR, size: 4096 });
|
|
89
|
-
}
|
|
90
|
-
if (handle instanceof FileSystemFileHandle) {
|
|
91
|
-
const { lastModified, size } = await handle.getFile();
|
|
92
|
-
return new Stats({ mode: 0o777 | constants.S_IFREG, size, mtimeMs: lastModified });
|
|
93
|
-
}
|
|
94
|
-
throw new ErrnoError(Errno.EBADE, 'Handle is not a directory or file', path, 'stat');
|
|
155
|
+
return inode.toStats();
|
|
95
156
|
}
|
|
96
|
-
async
|
|
97
|
-
const handle =
|
|
98
|
-
if (
|
|
99
|
-
throw ErrnoError.With('
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
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);
|
|
107
167
|
}
|
|
108
|
-
async
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
111
|
-
throw ErrnoError.With('
|
|
112
|
-
|
|
113
|
-
await handle.removeEntry(basename(path), { recursive: true }).catch((ex) => {
|
|
114
|
-
throw convertException(ex, path, 'unlink');
|
|
115
|
-
});
|
|
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());
|
|
116
173
|
}
|
|
117
|
-
|
|
174
|
+
/**
|
|
175
|
+
* @todo Implement
|
|
176
|
+
*/
|
|
118
177
|
async link(srcpath) {
|
|
119
178
|
return;
|
|
120
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
|
+
}
|
|
121
197
|
async rmdir(path) {
|
|
122
|
-
|
|
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);
|
|
123
205
|
}
|
|
124
206
|
async mkdir(path, mode, options) {
|
|
125
|
-
|
|
126
|
-
if (ex.code != 'ENOENT') {
|
|
127
|
-
throw ex;
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
if (existingHandle) {
|
|
207
|
+
if (this.index.has(path))
|
|
131
208
|
throw ErrnoError.With('EEXIST', path, 'mkdir');
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
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 }));
|
|
138
213
|
}
|
|
139
214
|
async readdir(path) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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');
|
|
144
224
|
const entries = [];
|
|
145
225
|
for await (const k of handle.keys()) {
|
|
146
226
|
entries.push(k);
|
|
147
227
|
}
|
|
148
228
|
return entries;
|
|
149
229
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (!(handle instanceof FileSystemDirectoryHandle)) {
|
|
158
|
-
throw ErrnoError.With('ENOTDIR', walked, 'getHandle');
|
|
159
|
-
}
|
|
160
|
-
walked = join(walked, part);
|
|
161
|
-
const child = await handle.getDirectoryHandle(part).catch((ex) => {
|
|
162
|
-
switch (ex.name) {
|
|
163
|
-
case 'TypeMismatchError':
|
|
164
|
-
return handle.getFileHandle(part).catch((ex) => {
|
|
165
|
-
//throw convertException(ex, walked, 'getHandle');
|
|
166
|
-
});
|
|
167
|
-
case 'TypeError':
|
|
168
|
-
throw new ErrnoError(Errno.ENOENT, ex.message, walked, 'getHandle');
|
|
169
|
-
default:
|
|
170
|
-
throw convertException(ex, walked, 'getHandle');
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
if (child)
|
|
174
|
-
this._handles.set(walked, child);
|
|
175
|
-
}
|
|
176
|
-
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;
|
|
177
237
|
}
|
|
178
238
|
}
|
|
179
239
|
const _WebAccess = {
|
|
180
240
|
name: 'WebAccess',
|
|
181
241
|
options: {
|
|
182
242
|
handle: { type: 'object', required: true },
|
|
243
|
+
metadata: { type: 'string', required: false },
|
|
183
244
|
},
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
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;
|
|
189
250
|
},
|
|
190
251
|
};
|
|
191
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,19 +1,19 @@
|
|
|
1
|
-
import { constants, decodeRaw, encodeRaw, Errno, ErrnoError, FileSystem,
|
|
1
|
+
import { _inode_fields, constants, decodeRaw, encodeRaw, Errno, ErrnoError, FileSystem, LazyFile, Stats, Sync } from '@zenfs/core';
|
|
2
2
|
import { basename, dirname } from '@zenfs/core/vfs/path.js';
|
|
3
|
-
const statsLikeKeys = ['size', 'mode', 'atimeMs', 'mtimeMs', 'ctimeMs', 'birthtimeMs', 'uid', 'gid', 'ino', 'nlink'];
|
|
4
3
|
function get_stats(node) {
|
|
5
4
|
const stats = {};
|
|
6
|
-
for (const key of
|
|
5
|
+
for (const key of _inode_fields) {
|
|
7
6
|
const value = node.getAttribute(key);
|
|
8
|
-
|
|
7
|
+
if (value !== null && value !== undefined)
|
|
8
|
+
stats[key] = parseInt(value, 16);
|
|
9
9
|
}
|
|
10
10
|
return new Stats(stats);
|
|
11
11
|
}
|
|
12
12
|
function set_stats(node, stats) {
|
|
13
|
-
for (const key of
|
|
14
|
-
if (stats[key]
|
|
15
|
-
|
|
16
|
-
|
|
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));
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
function get_paths(node, contents = false) {
|
|
@@ -30,11 +30,12 @@ function get_paths(node, contents = false) {
|
|
|
30
30
|
export class XMLFS extends Sync(FileSystem) {
|
|
31
31
|
constructor(
|
|
32
32
|
/**
|
|
33
|
-
* @
|
|
33
|
+
* @inheritDoc XMLOptions.root
|
|
34
34
|
*/
|
|
35
35
|
root = new DOMParser().parseFromString('<fs></fs>', 'application/xml').documentElement) {
|
|
36
|
-
super();
|
|
36
|
+
super(0x20786d6c, 'xmltmpfs');
|
|
37
37
|
this.root = root;
|
|
38
|
+
this.attributes.set('setid');
|
|
38
39
|
try {
|
|
39
40
|
this.mkdirSync('/', 0o777, { uid: 0, gid: 0 });
|
|
40
41
|
}
|
|
@@ -44,9 +45,6 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
44
45
|
throw error;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
metadata() {
|
|
48
|
-
return { ...super.metadata(), features: ['setid'] };
|
|
49
|
-
}
|
|
50
48
|
renameSync(oldPath, newPath) {
|
|
51
49
|
const node = this.get('rename', oldPath);
|
|
52
50
|
this.remove('rename', node, oldPath);
|
|
@@ -57,7 +55,7 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
57
55
|
}
|
|
58
56
|
openFileSync(path, flag) {
|
|
59
57
|
const node = this.get('openFile', path);
|
|
60
|
-
return new
|
|
58
|
+
return new LazyFile(this, path, flag, get_stats(node));
|
|
61
59
|
}
|
|
62
60
|
createFileSync(path, flag, mode, { uid, gid }) {
|
|
63
61
|
const parent = this.statSync(dirname(path));
|
|
@@ -67,7 +65,7 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
67
65
|
gid: parent.mode & constants.S_ISGID ? parent.gid : gid,
|
|
68
66
|
});
|
|
69
67
|
this.create('createFile', path, stats);
|
|
70
|
-
return new
|
|
68
|
+
return new LazyFile(this, path, flag, stats);
|
|
71
69
|
}
|
|
72
70
|
unlinkSync(path) {
|
|
73
71
|
const node = this.get('unlink', path);
|
|
@@ -107,11 +105,23 @@ export class XMLFS extends Sync(FileSystem) {
|
|
|
107
105
|
const node = this.get('link', target);
|
|
108
106
|
this.add('link', node, link);
|
|
109
107
|
}
|
|
110
|
-
syncSync(path, data, stats) {
|
|
108
|
+
syncSync(path, data, stats = {}) {
|
|
111
109
|
const node = this.get('sync', path);
|
|
112
|
-
|
|
110
|
+
if (data)
|
|
111
|
+
node.textContent = decodeRaw(data);
|
|
113
112
|
set_stats(node, stats);
|
|
114
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
|
+
}
|
|
115
125
|
toString() {
|
|
116
126
|
return new XMLSerializer().serializeToString(this.root);
|
|
117
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"]
|