@pistonite/pure 0.0.12
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/LICENSE +21 -0
- package/README.md +8 -0
- package/package.json +33 -0
- package/src/fs/FsError.ts +55 -0
- package/src/fs/FsFile.ts +67 -0
- package/src/fs/FsFileImpl.ts +225 -0
- package/src/fs/FsFileMgr.ts +29 -0
- package/src/fs/FsFileSystem.ts +71 -0
- package/src/fs/FsFileSystemInternal.ts +30 -0
- package/src/fs/FsImplEntryAPI.ts +188 -0
- package/src/fs/FsImplFileAPI.ts +126 -0
- package/src/fs/FsImplHandleAPI.ts +237 -0
- package/src/fs/FsOpen.ts +307 -0
- package/src/fs/FsPath.ts +137 -0
- package/src/fs/FsSave.ts +12 -0
- package/src/fs/FsSupportStatus.ts +91 -0
- package/src/fs/index.ts +129 -0
- package/src/log/index.ts +56 -0
- package/src/pref/dark.ts +184 -0
- package/src/pref/index.ts +12 -0
- package/src/pref/injectStyle.ts +22 -0
- package/src/pref/locale.ts +341 -0
- package/src/result/index.ts +215 -0
- package/src/sync/Debounce.ts +35 -0
- package/src/sync/Latest.ts +75 -0
- package/src/sync/RwLock.ts +95 -0
- package/src/sync/Serial.ts +170 -0
- package/src/sync/index.ts +12 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { type Ok, tryAsync, errstr } from "../result/index.ts";
|
|
2
|
+
|
|
3
|
+
import { FsErr, type FsResult, type FsVoid, fsErr, fsFail } from "./FsError.ts";
|
|
4
|
+
import type {
|
|
5
|
+
FsFileSystem,
|
|
6
|
+
FsFileSystemUninit,
|
|
7
|
+
FsCapabilities,
|
|
8
|
+
} from "./FsFileSystem.ts";
|
|
9
|
+
import type { FsFile } from "./FsFile.ts";
|
|
10
|
+
import { fsIsRoot, fsNormalize } from "./FsPath.ts";
|
|
11
|
+
import { FsFileMgr } from "./FsFileMgr.ts";
|
|
12
|
+
import type { FsFileSystemInternal } from "./FsFileSystemInternal.ts";
|
|
13
|
+
|
|
14
|
+
/** FsFileSystem implementation that uses FileEntry API */
|
|
15
|
+
export class FsImplEntryAPI
|
|
16
|
+
implements FsFileSystemUninit, FsFileSystem, FsFileSystemInternal
|
|
17
|
+
{
|
|
18
|
+
public root: string;
|
|
19
|
+
public capabilities: FsCapabilities;
|
|
20
|
+
|
|
21
|
+
private rootEntry: FileSystemDirectoryEntry;
|
|
22
|
+
|
|
23
|
+
private mgr: FsFileMgr;
|
|
24
|
+
|
|
25
|
+
constructor(root: string, rootEntry: FileSystemDirectoryEntry) {
|
|
26
|
+
this.root = root;
|
|
27
|
+
this.rootEntry = rootEntry;
|
|
28
|
+
this.capabilities = {
|
|
29
|
+
write: false,
|
|
30
|
+
live: true,
|
|
31
|
+
};
|
|
32
|
+
this.mgr = new FsFileMgr();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public init(): Promise<FsResult<FsFileSystem>> {
|
|
36
|
+
// no init needed
|
|
37
|
+
return Promise.resolve({ val: this });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async listDir(path: string): Promise<FsResult<string[]>> {
|
|
41
|
+
const normalized = fsNormalize(path);
|
|
42
|
+
if (normalized.err) {
|
|
43
|
+
return normalized;
|
|
44
|
+
}
|
|
45
|
+
path = normalized.val;
|
|
46
|
+
|
|
47
|
+
const entry = await this.resolveDir(path);
|
|
48
|
+
if (entry.err) {
|
|
49
|
+
return entry;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const entries = await tryAsync(
|
|
53
|
+
() =>
|
|
54
|
+
new Promise<FileSystemEntry[]>((resolve, reject) => {
|
|
55
|
+
entry.val.createReader().readEntries(resolve, reject);
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
if ("err" in entries) {
|
|
59
|
+
const err = fsFail(
|
|
60
|
+
"Failed to list directory `" +
|
|
61
|
+
path +
|
|
62
|
+
"`: " +
|
|
63
|
+
errstr(entries.err),
|
|
64
|
+
);
|
|
65
|
+
return { err };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const names = entries.val.map(({ isDirectory, name }) => {
|
|
69
|
+
if (isDirectory) {
|
|
70
|
+
return name + "/";
|
|
71
|
+
}
|
|
72
|
+
return name;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return { val: names };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public async read(path: string): Promise<FsResult<File>> {
|
|
79
|
+
const normalized = fsNormalize(path);
|
|
80
|
+
if (normalized.err) {
|
|
81
|
+
return normalized;
|
|
82
|
+
}
|
|
83
|
+
path = normalized.val;
|
|
84
|
+
|
|
85
|
+
const entry = await this.resolveFile(path);
|
|
86
|
+
if (entry.err) {
|
|
87
|
+
return entry;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const file = await tryAsync(
|
|
91
|
+
() =>
|
|
92
|
+
new Promise<File>((resolve, reject) => {
|
|
93
|
+
entry.val.file(resolve, reject);
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
if ("err" in file) {
|
|
97
|
+
const err = fsFail(
|
|
98
|
+
"Failed to read file `" + path + "`: " + errstr(file.err),
|
|
99
|
+
);
|
|
100
|
+
return { err };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return file;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public write(): Promise<FsVoid> {
|
|
107
|
+
const err = fsErr(
|
|
108
|
+
FsErr.NotSupported,
|
|
109
|
+
"Write not supported in FileEntry API",
|
|
110
|
+
);
|
|
111
|
+
return Promise.resolve({ err });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public getFile(path: string): FsFile {
|
|
115
|
+
return this.mgr.get(this, path);
|
|
116
|
+
}
|
|
117
|
+
public getOpenedPaths(): string[] {
|
|
118
|
+
return this.mgr.getOpenedPaths();
|
|
119
|
+
}
|
|
120
|
+
public closeFile(path: string): void {
|
|
121
|
+
this.mgr.close(path);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Resolve a directory entry. Path must be normalized */
|
|
125
|
+
private async resolveDir(
|
|
126
|
+
path: string,
|
|
127
|
+
): Promise<FsResult<FileSystemDirectoryEntry>> {
|
|
128
|
+
if (fsIsRoot(path)) {
|
|
129
|
+
return { val: this.rootEntry };
|
|
130
|
+
}
|
|
131
|
+
const entry = await tryAsync(
|
|
132
|
+
() =>
|
|
133
|
+
new Promise<FileSystemEntry>((resolve, reject) => {
|
|
134
|
+
this.rootEntry.getDirectory(path, {}, resolve, reject);
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
if ("err" in entry) {
|
|
138
|
+
const err = fsFail(
|
|
139
|
+
"Failed to resolve directory `" +
|
|
140
|
+
path +
|
|
141
|
+
"`: " +
|
|
142
|
+
errstr(entry.err),
|
|
143
|
+
);
|
|
144
|
+
return { err };
|
|
145
|
+
}
|
|
146
|
+
if (!entry.val.isDirectory) {
|
|
147
|
+
const err = fsErr(
|
|
148
|
+
FsErr.IsFile,
|
|
149
|
+
"Path `" + path + "` is not a directory",
|
|
150
|
+
);
|
|
151
|
+
return { err };
|
|
152
|
+
}
|
|
153
|
+
return entry as Ok<FileSystemDirectoryEntry>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Resolve a file entry. Path must be normalized */
|
|
157
|
+
private async resolveFile(
|
|
158
|
+
path: string,
|
|
159
|
+
): Promise<FsResult<FileSystemFileEntry>> {
|
|
160
|
+
if (fsIsRoot(path)) {
|
|
161
|
+
const err = fsErr(
|
|
162
|
+
FsErr.IsDirectory,
|
|
163
|
+
"Path `" + path + "` is not a file",
|
|
164
|
+
);
|
|
165
|
+
return { err };
|
|
166
|
+
}
|
|
167
|
+
const entry = await tryAsync(
|
|
168
|
+
() =>
|
|
169
|
+
new Promise<FileSystemEntry>((resolve, reject) => {
|
|
170
|
+
this.rootEntry.getFile(path, {}, resolve, reject);
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
if ("err" in entry) {
|
|
174
|
+
const err = fsFail(
|
|
175
|
+
"Failed to resolve file `" + path + "`: " + errstr(entry.err),
|
|
176
|
+
);
|
|
177
|
+
return { err };
|
|
178
|
+
}
|
|
179
|
+
if (!entry.val.isFile) {
|
|
180
|
+
const err = fsErr(
|
|
181
|
+
FsErr.IsDirectory,
|
|
182
|
+
"Path `" + path + "` is not a file",
|
|
183
|
+
);
|
|
184
|
+
return { err };
|
|
185
|
+
}
|
|
186
|
+
return entry as Ok<FileSystemFileEntry>;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { FsFile } from "./FsFile.ts";
|
|
2
|
+
import type {
|
|
3
|
+
FsFileSystem,
|
|
4
|
+
FsFileSystemUninit,
|
|
5
|
+
FsCapabilities,
|
|
6
|
+
} from "./FsFileSystem.ts";
|
|
7
|
+
import { FsErr, type FsResult, type FsVoid, fsErr } from "./FsError.ts";
|
|
8
|
+
import { fsIsRoot, fsNormalize } from "./FsPath.ts";
|
|
9
|
+
import { FsFileMgr } from "./FsFileMgr.ts";
|
|
10
|
+
import type { FsFileSystemInternal } from "./FsFileSystemInternal.ts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* FileSystem implementation that uses a list of Files
|
|
14
|
+
* This is supported in all browsers, but it is stale.
|
|
15
|
+
* It's used for Firefox when the File Entries API is not available
|
|
16
|
+
* i.e. opened from <input type="file">
|
|
17
|
+
*/
|
|
18
|
+
export class FsImplFileAPI
|
|
19
|
+
implements FsFileSystemUninit, FsFileSystem, FsFileSystemInternal
|
|
20
|
+
{
|
|
21
|
+
public root: string;
|
|
22
|
+
public capabilities: FsCapabilities;
|
|
23
|
+
|
|
24
|
+
private files: Record<string, File>;
|
|
25
|
+
private directories: Record<string, string[]>;
|
|
26
|
+
private mgr: FsFileMgr;
|
|
27
|
+
|
|
28
|
+
constructor(files: FileList) {
|
|
29
|
+
// this seems to also work for windows
|
|
30
|
+
this.root = files[0].webkitRelativePath.split("/", 1)[0];
|
|
31
|
+
this.capabilities = {
|
|
32
|
+
write: false,
|
|
33
|
+
live: false,
|
|
34
|
+
};
|
|
35
|
+
this.files = {};
|
|
36
|
+
this.directories = {};
|
|
37
|
+
this.mgr = new FsFileMgr();
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < files.length; i++) {
|
|
40
|
+
const file = files[i];
|
|
41
|
+
// remove "<root>/"
|
|
42
|
+
const path = file.webkitRelativePath.slice(this.root.length + 1);
|
|
43
|
+
const normalized = fsNormalize(path);
|
|
44
|
+
if (normalized.err) {
|
|
45
|
+
// shouldn't happen since the path is from the File API
|
|
46
|
+
console.error("Invalid path: " + path);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
this.files[normalized.val] = file;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public init(): Promise<FsResult<FsFileSystem>> {
|
|
54
|
+
// no init needed
|
|
55
|
+
return Promise.resolve({ val: this });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public listDir(path: string): Promise<FsResult<string[]>> {
|
|
59
|
+
const normalized = fsNormalize(path);
|
|
60
|
+
if (normalized.err) {
|
|
61
|
+
return Promise.resolve(normalized);
|
|
62
|
+
}
|
|
63
|
+
path = normalized.val;
|
|
64
|
+
|
|
65
|
+
if (path in this.directories) {
|
|
66
|
+
return Promise.resolve({ val: this.directories[path] });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const set = new Set<string>();
|
|
70
|
+
const prefix = fsIsRoot(path) ? "" : path + "/";
|
|
71
|
+
|
|
72
|
+
Object.keys(this.files).forEach((path) => {
|
|
73
|
+
if (!path.startsWith(prefix)) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const relPath = path.slice(prefix.length);
|
|
77
|
+
const slashIndex = relPath.indexOf("/");
|
|
78
|
+
if (slashIndex < 0) {
|
|
79
|
+
// file
|
|
80
|
+
set.add(relPath);
|
|
81
|
+
} else {
|
|
82
|
+
// directory
|
|
83
|
+
const dir = relPath.slice(0, slashIndex + 1);
|
|
84
|
+
set.add(dir);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const paths = Array.from(set);
|
|
89
|
+
this.directories[path] = paths;
|
|
90
|
+
|
|
91
|
+
return Promise.resolve({ val: paths });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public read(path: string): Promise<FsResult<File>> {
|
|
95
|
+
const normalized = fsNormalize(path);
|
|
96
|
+
if (normalized.err) {
|
|
97
|
+
return Promise.resolve(normalized);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const file = this.files[normalized.val];
|
|
101
|
+
if (!file) {
|
|
102
|
+
const err = fsErr(FsErr.NotFound, "File not found: " + path);
|
|
103
|
+
return Promise.resolve({ err });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return Promise.resolve({ val: file });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public write(): Promise<FsVoid> {
|
|
110
|
+
const err = fsErr(
|
|
111
|
+
FsErr.NotSupported,
|
|
112
|
+
"Write not supported in File API",
|
|
113
|
+
);
|
|
114
|
+
return Promise.resolve({ err });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public getFile(path: string): FsFile {
|
|
118
|
+
return this.mgr.get(this, path);
|
|
119
|
+
}
|
|
120
|
+
public getOpenedPaths(): string[] {
|
|
121
|
+
return this.mgr.getOpenedPaths();
|
|
122
|
+
}
|
|
123
|
+
public closeFile(path: string): void {
|
|
124
|
+
this.mgr.close(path);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FsFileSystem implementation for FileSystemAccess API
|
|
3
|
+
* @module
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { tryAsync, errstr } from "../result/index.ts";
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
FsFileSystem,
|
|
10
|
+
FsFileSystemUninit,
|
|
11
|
+
FsCapabilities,
|
|
12
|
+
} from "./FsFileSystem.ts";
|
|
13
|
+
import { FsErr, type FsResult, type FsVoid, fsErr, fsFail } from "./FsError.ts";
|
|
14
|
+
import type { FsFile } from "./FsFile.ts";
|
|
15
|
+
import {
|
|
16
|
+
fsComponents,
|
|
17
|
+
fsGetBase,
|
|
18
|
+
fsGetName,
|
|
19
|
+
fsIsRoot,
|
|
20
|
+
fsNormalize,
|
|
21
|
+
} from "./FsPath.ts";
|
|
22
|
+
import { FsFileMgr } from "./FsFileMgr.ts";
|
|
23
|
+
import type { FsFileSystemInternal } from "./FsFileSystemInternal.ts";
|
|
24
|
+
|
|
25
|
+
type PermissionStatus = "granted" | "denied" | "prompt";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* FsFileSystem implementation that uses FileSystem Access API
|
|
29
|
+
* This is only supported in Chrome/Edge
|
|
30
|
+
*/
|
|
31
|
+
export class FsImplHandleAPI
|
|
32
|
+
implements FsFileSystemUninit, FsFileSystem, FsFileSystemInternal
|
|
33
|
+
{
|
|
34
|
+
public root: string;
|
|
35
|
+
public capabilities: FsCapabilities;
|
|
36
|
+
/** If app requested write access */
|
|
37
|
+
private writeMode: boolean;
|
|
38
|
+
private rootHandle: FileSystemDirectoryHandle;
|
|
39
|
+
private permissionStatus: PermissionStatus;
|
|
40
|
+
|
|
41
|
+
private mgr: FsFileMgr;
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
rootPath: string,
|
|
45
|
+
rootHandle: FileSystemDirectoryHandle,
|
|
46
|
+
write: boolean,
|
|
47
|
+
) {
|
|
48
|
+
this.root = rootPath;
|
|
49
|
+
this.rootHandle = rootHandle;
|
|
50
|
+
this.writeMode = write;
|
|
51
|
+
this.permissionStatus = "prompt";
|
|
52
|
+
this.capabilities = {
|
|
53
|
+
write,
|
|
54
|
+
live: true,
|
|
55
|
+
};
|
|
56
|
+
this.mgr = new FsFileMgr();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async init(): Promise<FsResult<FsFileSystem>> {
|
|
60
|
+
// @ts-expect-error ts lib does not have requestPermission
|
|
61
|
+
this.permissionStatus = await this.rootHandle.requestPermission({
|
|
62
|
+
mode: this.writeMode ? "readwrite" : "read",
|
|
63
|
+
});
|
|
64
|
+
if (this.permissionStatus !== "granted") {
|
|
65
|
+
const err = fsErr(FsErr.PermissionDenied, "User denied permission");
|
|
66
|
+
return { err };
|
|
67
|
+
}
|
|
68
|
+
return { val: this };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public async listDir(path: string): Promise<FsResult<string[]>> {
|
|
72
|
+
const normalized = fsNormalize(path);
|
|
73
|
+
if (normalized.err) {
|
|
74
|
+
return normalized;
|
|
75
|
+
}
|
|
76
|
+
path = normalized.val;
|
|
77
|
+
|
|
78
|
+
const handle = await this.resolveDir(path);
|
|
79
|
+
if (handle.err) {
|
|
80
|
+
return handle;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const entries = await tryAsync(async () => {
|
|
84
|
+
const entries: string[] = [];
|
|
85
|
+
// @ts-expect-error ts lib does not have values()
|
|
86
|
+
for await (const entry of handle.val.values()) {
|
|
87
|
+
const { kind, name } = entry;
|
|
88
|
+
if (kind === "directory") {
|
|
89
|
+
entries.push(name + "/");
|
|
90
|
+
} else {
|
|
91
|
+
entries.push(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return entries;
|
|
95
|
+
});
|
|
96
|
+
if ("err" in entries) {
|
|
97
|
+
const err = fsFail(
|
|
98
|
+
"Error reading entries from directory `" +
|
|
99
|
+
path +
|
|
100
|
+
"`: " +
|
|
101
|
+
errstr(entries.err),
|
|
102
|
+
);
|
|
103
|
+
return { err };
|
|
104
|
+
}
|
|
105
|
+
return entries;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public async read(path: string): Promise<FsResult<File>> {
|
|
109
|
+
const normalized = fsNormalize(path);
|
|
110
|
+
if (normalized.err) {
|
|
111
|
+
return normalized;
|
|
112
|
+
}
|
|
113
|
+
path = normalized.val;
|
|
114
|
+
|
|
115
|
+
const handle = await this.resolveFile(path);
|
|
116
|
+
if (handle.err) {
|
|
117
|
+
return handle;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const file = await tryAsync(() => handle.val.getFile());
|
|
121
|
+
if ("err" in file) {
|
|
122
|
+
const err = fsFail(
|
|
123
|
+
"Failed to read file `" + path + "`: " + errstr(file.err),
|
|
124
|
+
);
|
|
125
|
+
return { err };
|
|
126
|
+
}
|
|
127
|
+
return file;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async write(path: string, content: Uint8Array): Promise<FsVoid> {
|
|
131
|
+
if (!this.writeMode) {
|
|
132
|
+
const err = fsErr(
|
|
133
|
+
FsErr.PermissionDenied,
|
|
134
|
+
"Write mode not requested",
|
|
135
|
+
);
|
|
136
|
+
return { err };
|
|
137
|
+
}
|
|
138
|
+
const normalized = fsNormalize(path);
|
|
139
|
+
if (normalized.err) {
|
|
140
|
+
return normalized;
|
|
141
|
+
}
|
|
142
|
+
path = normalized.val;
|
|
143
|
+
|
|
144
|
+
const handle = await this.resolveFile(path);
|
|
145
|
+
if (handle.err) {
|
|
146
|
+
return handle;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const result = await tryAsync(async () => {
|
|
150
|
+
const file = await handle.val.createWritable();
|
|
151
|
+
await file.write(content);
|
|
152
|
+
await file.close();
|
|
153
|
+
return {};
|
|
154
|
+
});
|
|
155
|
+
if ("err" in result) {
|
|
156
|
+
const err = fsFail(
|
|
157
|
+
"Failed to write file `" + path + "`: " + errstr(result.err),
|
|
158
|
+
);
|
|
159
|
+
return { err };
|
|
160
|
+
}
|
|
161
|
+
return {};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public getFile(path: string): FsFile {
|
|
165
|
+
return this.mgr.get(this, path);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public getOpenedPaths(): string[] {
|
|
169
|
+
return this.mgr.getOpenedPaths();
|
|
170
|
+
}
|
|
171
|
+
public closeFile(path: string): void {
|
|
172
|
+
this.mgr.close(path);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Resolve the FileSystemDirectoryHandle for a directory.
|
|
177
|
+
* The path must be normalized
|
|
178
|
+
*/
|
|
179
|
+
private async resolveDir(
|
|
180
|
+
path: string,
|
|
181
|
+
): Promise<FsResult<FileSystemDirectoryHandle>> {
|
|
182
|
+
if (fsIsRoot(path)) {
|
|
183
|
+
return { val: this.rootHandle };
|
|
184
|
+
}
|
|
185
|
+
let handle: FileSystemDirectoryHandle = this.rootHandle;
|
|
186
|
+
const parts: string[] = [];
|
|
187
|
+
for (const part of fsComponents(path)) {
|
|
188
|
+
parts.push(part);
|
|
189
|
+
const next = await tryAsync(() => handle.getDirectoryHandle(part));
|
|
190
|
+
if ("err" in next) {
|
|
191
|
+
const dir = parts.join("/");
|
|
192
|
+
const err = fsFail(
|
|
193
|
+
"Failed to resolve directory `" +
|
|
194
|
+
dir +
|
|
195
|
+
"`: " +
|
|
196
|
+
errstr(next.err),
|
|
197
|
+
);
|
|
198
|
+
return { err };
|
|
199
|
+
}
|
|
200
|
+
handle = next.val;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return { val: handle };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Resolve the FileSystemFileHandle for a file.
|
|
208
|
+
* The path must be normalized
|
|
209
|
+
*/
|
|
210
|
+
private async resolveFile(
|
|
211
|
+
path: string,
|
|
212
|
+
): Promise<FsResult<FileSystemFileHandle>> {
|
|
213
|
+
const parent = fsGetBase(path);
|
|
214
|
+
if (parent.err) {
|
|
215
|
+
return parent;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const name = fsGetName(path);
|
|
219
|
+
if (name.err) {
|
|
220
|
+
return name;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const handle = await this.resolveDir(parent.val);
|
|
224
|
+
if (handle.err) {
|
|
225
|
+
return handle;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const file = await tryAsync(() => handle.val.getFileHandle(name.val));
|
|
229
|
+
if ("err" in file) {
|
|
230
|
+
const err = fsFail(
|
|
231
|
+
"Failed to resolve file `" + path + "`: " + errstr(file.err),
|
|
232
|
+
);
|
|
233
|
+
return { err };
|
|
234
|
+
}
|
|
235
|
+
return file;
|
|
236
|
+
}
|
|
237
|
+
}
|