@statewalker/webrun-files-browser 0.5.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -98
- package/dist/browser-files-api.d.ts +19 -47
- package/dist/browser-files-api.d.ts.map +1 -1
- package/dist/cjs/index.cjs +161 -211
- package/dist/esm/index.js +149 -201
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/open-browser-files-api.d.ts +67 -0
- package/dist/open-browser-files-api.d.ts.map +1 -0
- package/package.json +15 -14
- package/src/browser-files-api.ts +123 -103
- package/src/index.ts +7 -4
- package/src/open-browser-files-api.ts +148 -0
- package/LICENSE +0 -21
- package/dist/browser-file-handle.d.ts +0 -42
- package/dist/browser-file-handle.d.ts.map +0 -1
- package/src/browser-file-handle.ts +0 -194
package/dist/esm/index.js
CHANGED
|
@@ -1,128 +1,17 @@
|
|
|
1
|
-
import { basename, dirname, joinPath,
|
|
2
|
-
|
|
3
|
-
//#region src/browser-file-handle.ts
|
|
4
|
-
var BrowserFileHandle = class {
|
|
5
|
-
fileHandle;
|
|
6
|
-
file;
|
|
7
|
-
_size;
|
|
8
|
-
closed = false;
|
|
9
|
-
constructor(options) {
|
|
10
|
-
this.fileHandle = options.fileHandle;
|
|
11
|
-
this.file = options.initialFile;
|
|
12
|
-
this._size = options.initialFile.size;
|
|
13
|
-
}
|
|
14
|
-
get size() {
|
|
15
|
-
return this._size;
|
|
16
|
-
}
|
|
17
|
-
async close() {
|
|
18
|
-
this.closed = true;
|
|
19
|
-
this.file = null;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Appends data to the end of the file.
|
|
23
|
-
*/
|
|
24
|
-
async appendFile(data, options = {}) {
|
|
25
|
-
if (this.closed) throw new Error("FileHandle is closed");
|
|
26
|
-
const { signal } = options;
|
|
27
|
-
const writable = await this.fileHandle.createWritable({ keepExistingData: true });
|
|
28
|
-
let bytesWritten = 0;
|
|
29
|
-
try {
|
|
30
|
-
await writable.seek(this._size);
|
|
31
|
-
for await (const chunk of toBinaryAsyncIterable(data)) {
|
|
32
|
-
if (signal?.aborted) throw new Error("Operation aborted");
|
|
33
|
-
await writable.write(chunk);
|
|
34
|
-
bytesWritten += chunk.length;
|
|
35
|
-
}
|
|
36
|
-
} finally {
|
|
37
|
-
await writable.close();
|
|
38
|
-
}
|
|
39
|
-
this.file = await this.fileHandle.getFile();
|
|
40
|
-
this._size = this.file.size;
|
|
41
|
-
return bytesWritten;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Creates a read stream for the file.
|
|
45
|
-
* Uses File.slice() for efficient partial reads.
|
|
46
|
-
*/
|
|
47
|
-
async *createReadStream(options = {}) {
|
|
48
|
-
if (this.closed) throw new Error("FileHandle is closed");
|
|
49
|
-
if (!this.file) {
|
|
50
|
-
this.file = await this.fileHandle.getFile();
|
|
51
|
-
this._size = this.file.size;
|
|
52
|
-
}
|
|
53
|
-
const { start = 0, end = Infinity, signal } = options;
|
|
54
|
-
const bufferSize = 8192;
|
|
55
|
-
const actualEnd = Math.min(end, this._size);
|
|
56
|
-
if (start >= actualEnd || this._size === 0) return;
|
|
57
|
-
let position = start;
|
|
58
|
-
while (position < actualEnd) {
|
|
59
|
-
if (signal?.aborted) throw new Error("Operation aborted");
|
|
60
|
-
const remaining = actualEnd - position;
|
|
61
|
-
const toRead = Math.min(bufferSize, remaining);
|
|
62
|
-
const buffer = await this.file.slice(position, position + toRead).arrayBuffer();
|
|
63
|
-
yield new Uint8Array(buffer);
|
|
64
|
-
position += buffer.byteLength;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Writes data to the file starting at the specified position.
|
|
69
|
-
* If start > 0, preserves content before start position.
|
|
70
|
-
* Truncates file at start position before writing.
|
|
71
|
-
*/
|
|
72
|
-
async createWriteStream(data, options = {}) {
|
|
73
|
-
if (this.closed) throw new Error("FileHandle is closed");
|
|
74
|
-
const { start = 0, signal } = options;
|
|
75
|
-
const writable = await this.fileHandle.createWritable({ keepExistingData: start > 0 });
|
|
76
|
-
let bytesWritten = 0;
|
|
77
|
-
try {
|
|
78
|
-
if (start > 0) {
|
|
79
|
-
await writable.seek(start);
|
|
80
|
-
await writable.truncate(start);
|
|
81
|
-
}
|
|
82
|
-
for await (const chunk of toBinaryAsyncIterable(data)) {
|
|
83
|
-
if (signal?.aborted) throw new Error("Operation aborted");
|
|
84
|
-
await writable.write(chunk);
|
|
85
|
-
bytesWritten += chunk.length;
|
|
86
|
-
}
|
|
87
|
-
} finally {
|
|
88
|
-
await writable.close();
|
|
89
|
-
}
|
|
90
|
-
this.file = await this.fileHandle.getFile();
|
|
91
|
-
this._size = this.file.size;
|
|
92
|
-
return bytesWritten;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Random access read from file at a specific position.
|
|
96
|
-
* Uses File.slice() for efficient partial reads.
|
|
97
|
-
*/
|
|
98
|
-
async read(buffer, offset, length, position) {
|
|
99
|
-
if (this.closed) throw new Error("FileHandle is closed");
|
|
100
|
-
if (!this.file) {
|
|
101
|
-
this.file = await this.fileHandle.getFile();
|
|
102
|
-
this._size = this.file.size;
|
|
103
|
-
}
|
|
104
|
-
let len = Math.min(length, buffer.length - offset);
|
|
105
|
-
len = Math.min(len, this._size - position);
|
|
106
|
-
len = Math.max(0, len);
|
|
107
|
-
if (len === 0) return 0;
|
|
108
|
-
const arrayBuffer = await this.file.slice(position, position + len).arrayBuffer();
|
|
109
|
-
const data = new Uint8Array(arrayBuffer);
|
|
110
|
-
buffer.set(data, offset);
|
|
111
|
-
return len;
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
//#endregion
|
|
1
|
+
import { basename, dirname, joinPath, normalizePath } from "@statewalker/webrun-files";
|
|
116
2
|
//#region src/browser-files-api.ts
|
|
3
|
+
/**
|
|
4
|
+
* Browser filesystem implementation using the File System Access API.
|
|
5
|
+
*
|
|
6
|
+
* Provides FilesApi interface over browser's FileSystemDirectoryHandle.
|
|
7
|
+
* This enables web applications to read/write files in user-selected directories
|
|
8
|
+
* or the Origin Private File System (OPFS).
|
|
9
|
+
*/
|
|
117
10
|
var BrowserFilesApi = class {
|
|
118
11
|
rootHandle;
|
|
119
12
|
constructor(options) {
|
|
120
13
|
this.rootHandle = options.rootHandle;
|
|
121
14
|
}
|
|
122
|
-
/**
|
|
123
|
-
* Navigates to a directory handle from a path.
|
|
124
|
-
* Returns null if the directory doesn't exist and create is false.
|
|
125
|
-
*/
|
|
126
15
|
async getDirectoryHandle(path, options = {}) {
|
|
127
16
|
const segments = path.split("/").filter((s) => s.length > 0);
|
|
128
17
|
let current = this.rootHandle;
|
|
@@ -133,12 +22,8 @@ var BrowserFilesApi = class {
|
|
|
133
22
|
}
|
|
134
23
|
return current;
|
|
135
24
|
}
|
|
136
|
-
/**
|
|
137
|
-
* Gets a file handle from a path.
|
|
138
|
-
* Returns null if the file doesn't exist and create is false.
|
|
139
|
-
*/
|
|
140
25
|
async getFileHandle(path, options = {}) {
|
|
141
|
-
const normalized =
|
|
26
|
+
const normalized = normalizePath(path);
|
|
142
27
|
const parentPath = dirname(normalized);
|
|
143
28
|
const fileName = basename(normalized);
|
|
144
29
|
const parent = await this.getDirectoryHandle(parentPath, options);
|
|
@@ -149,14 +34,51 @@ var BrowserFilesApi = class {
|
|
|
149
34
|
return null;
|
|
150
35
|
}
|
|
151
36
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
37
|
+
async *read(path, options) {
|
|
38
|
+
const fileHandle = await this.getFileHandle(path);
|
|
39
|
+
if (!fileHandle) return;
|
|
40
|
+
try {
|
|
41
|
+
const file = await fileHandle.getFile();
|
|
42
|
+
const start = options?.start ?? 0;
|
|
43
|
+
const length = options?.length;
|
|
44
|
+
const end = length !== void 0 ? start + length : file.size;
|
|
45
|
+
if (start >= file.size) return;
|
|
46
|
+
const bufferSize = 8192;
|
|
47
|
+
let position = start;
|
|
48
|
+
const actualEnd = Math.min(end, file.size);
|
|
49
|
+
while (position < actualEnd) {
|
|
50
|
+
const remaining = actualEnd - position;
|
|
51
|
+
const toRead = Math.min(bufferSize, remaining);
|
|
52
|
+
const buffer = await file.slice(position, position + toRead).arrayBuffer();
|
|
53
|
+
yield new Uint8Array(buffer);
|
|
54
|
+
position += buffer.byteLength;
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async write(path, content) {
|
|
61
|
+
const normalized = normalizePath(path);
|
|
62
|
+
const parentPath = dirname(normalized);
|
|
63
|
+
const fileName = basename(normalized);
|
|
64
|
+
const parent = await this.getDirectoryHandle(parentPath, { create: true });
|
|
65
|
+
if (!parent) throw new Error(`Cannot create directory: ${parentPath}`);
|
|
66
|
+
const writable = await (await parent.getFileHandle(fileName, { create: true })).createWritable();
|
|
67
|
+
try {
|
|
68
|
+
for await (const chunk of content) await writable.write(chunk);
|
|
69
|
+
} finally {
|
|
70
|
+
await writable.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async mkdir(path) {
|
|
74
|
+
const normalized = normalizePath(path);
|
|
75
|
+
await this.getDirectoryHandle(normalized, { create: true });
|
|
76
|
+
}
|
|
77
|
+
async *list(path, options) {
|
|
78
|
+
const normalized = normalizePath(path);
|
|
157
79
|
const dirHandle = await this.getDirectoryHandle(normalized);
|
|
158
80
|
if (!dirHandle) return;
|
|
159
|
-
const
|
|
81
|
+
const recursive = options?.recursive ?? false;
|
|
160
82
|
for await (const [name, handle] of dirHandle.entries()) {
|
|
161
83
|
const entryPath = joinPath(normalized, name);
|
|
162
84
|
const isDirectory = handle.kind === "directory";
|
|
@@ -167,25 +89,18 @@ var BrowserFilesApi = class {
|
|
|
167
89
|
lastModified: 0
|
|
168
90
|
};
|
|
169
91
|
if (!isDirectory) try {
|
|
170
|
-
const file
|
|
171
|
-
info.size = file
|
|
172
|
-
info.lastModified = file
|
|
173
|
-
info.type = file$1.type;
|
|
92
|
+
const file = await handle.getFile();
|
|
93
|
+
info.size = file.size;
|
|
94
|
+
info.lastModified = file.lastModified;
|
|
174
95
|
} catch {}
|
|
175
96
|
yield info;
|
|
176
97
|
if (recursive && isDirectory) yield* this.list(entryPath, options);
|
|
177
98
|
}
|
|
178
99
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*/
|
|
182
|
-
async stats(file) {
|
|
183
|
-
const normalized = resolveFileRef(file);
|
|
184
|
-
const fileName = basename(normalized);
|
|
100
|
+
async stats(path) {
|
|
101
|
+
const normalized = normalizePath(path);
|
|
185
102
|
if (normalized === "/") return {
|
|
186
103
|
kind: "directory",
|
|
187
|
-
name: "",
|
|
188
|
-
path: "/",
|
|
189
104
|
lastModified: 0
|
|
190
105
|
};
|
|
191
106
|
const fileHandle = await this.getFileHandle(normalized);
|
|
@@ -193,27 +108,22 @@ var BrowserFilesApi = class {
|
|
|
193
108
|
const f = await fileHandle.getFile();
|
|
194
109
|
return {
|
|
195
110
|
kind: "file",
|
|
196
|
-
name: fileName,
|
|
197
|
-
path: normalized,
|
|
198
111
|
size: f.size,
|
|
199
|
-
lastModified: f.lastModified
|
|
200
|
-
type: f.type
|
|
112
|
+
lastModified: f.lastModified
|
|
201
113
|
};
|
|
202
114
|
} catch {
|
|
203
115
|
return;
|
|
204
116
|
}
|
|
205
117
|
if (await this.getDirectoryHandle(normalized)) return {
|
|
206
118
|
kind: "directory",
|
|
207
|
-
name: fileName,
|
|
208
|
-
path: normalized,
|
|
209
119
|
lastModified: 0
|
|
210
120
|
};
|
|
211
121
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
async remove(
|
|
216
|
-
const normalized =
|
|
122
|
+
async exists(path) {
|
|
123
|
+
return await this.stats(path) !== void 0;
|
|
124
|
+
}
|
|
125
|
+
async remove(path) {
|
|
126
|
+
const normalized = normalizePath(path);
|
|
217
127
|
const parentPath = dirname(normalized);
|
|
218
128
|
const name = basename(normalized);
|
|
219
129
|
const parent = await this.getDirectoryHandle(parentPath);
|
|
@@ -225,47 +135,30 @@ var BrowserFilesApi = class {
|
|
|
225
135
|
return false;
|
|
226
136
|
}
|
|
227
137
|
}
|
|
228
|
-
/**
|
|
229
|
-
* Opens a file for reading and writing.
|
|
230
|
-
* Creates the file and parent directories if they don't exist.
|
|
231
|
-
*/
|
|
232
|
-
async open(file) {
|
|
233
|
-
const normalized = resolveFileRef(file);
|
|
234
|
-
const parentPath = dirname(normalized);
|
|
235
|
-
const fileName = basename(normalized);
|
|
236
|
-
const parent = await this.getDirectoryHandle(parentPath, { create: true });
|
|
237
|
-
if (!parent) throw new Error(`Cannot create directory: ${parentPath}`);
|
|
238
|
-
const fileHandle = await parent.getFileHandle(fileName, { create: true });
|
|
239
|
-
return new BrowserFileHandle({
|
|
240
|
-
fileHandle,
|
|
241
|
-
initialFile: await fileHandle.getFile()
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Creates a directory (and parent directories if needed).
|
|
246
|
-
*/
|
|
247
|
-
async mkdir(file) {
|
|
248
|
-
const normalized = resolveFileRef(file);
|
|
249
|
-
await this.getDirectoryHandle(normalized, { create: true });
|
|
250
|
-
}
|
|
251
|
-
/**
|
|
252
|
-
* Moves a file or directory using copy + delete.
|
|
253
|
-
* The File System Access API doesn't have a native move operation.
|
|
254
|
-
*/
|
|
255
138
|
async move(source, target) {
|
|
139
|
+
const sourcePath = normalizePath(source);
|
|
140
|
+
const targetPath = normalizePath(target);
|
|
141
|
+
const targetParentPath = dirname(targetPath);
|
|
142
|
+
const targetName = basename(targetPath);
|
|
143
|
+
const sourceStats = await this.stats(sourcePath);
|
|
144
|
+
if (!sourceStats) return false;
|
|
145
|
+
const sourceHandle = sourceStats.kind === "file" ? await this.getFileHandle(sourcePath) : await this.getDirectoryHandle(sourcePath);
|
|
146
|
+
if (!sourceHandle) return false;
|
|
147
|
+
if (typeof sourceHandle.move === "function") try {
|
|
148
|
+
const targetParent = await this.getDirectoryHandle(targetParentPath, { create: true });
|
|
149
|
+
if (!targetParent) return false;
|
|
150
|
+
await sourceHandle.move(targetParent, targetName);
|
|
151
|
+
return true;
|
|
152
|
+
} catch {}
|
|
256
153
|
if (!await this.copy(source, target)) return false;
|
|
257
154
|
return this.remove(source);
|
|
258
155
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
const { recursive = true } = options;
|
|
266
|
-
const sourceInfo = await this.stats(source);
|
|
267
|
-
if (!sourceInfo) return false;
|
|
268
|
-
if (sourceInfo.kind === "file") {
|
|
156
|
+
async copy(source, target) {
|
|
157
|
+
const sourcePath = normalizePath(source);
|
|
158
|
+
const targetPath = normalizePath(target);
|
|
159
|
+
const sourceStats = await this.stats(source);
|
|
160
|
+
if (!sourceStats) return false;
|
|
161
|
+
if (sourceStats.kind === "file") {
|
|
269
162
|
const sourceHandle = await this.getFileHandle(sourcePath);
|
|
270
163
|
if (!sourceHandle) return false;
|
|
271
164
|
const sourceFile = await sourceHandle.getFile();
|
|
@@ -286,7 +179,6 @@ var BrowserFilesApi = class {
|
|
|
286
179
|
}
|
|
287
180
|
return true;
|
|
288
181
|
}
|
|
289
|
-
if (!recursive) return false;
|
|
290
182
|
await this.getDirectoryHandle(targetPath, { create: true });
|
|
291
183
|
for await (const entry of this.list(source, { recursive: true })) {
|
|
292
184
|
if (entry.kind === "directory") continue;
|
|
@@ -314,19 +206,75 @@ var BrowserFilesApi = class {
|
|
|
314
206
|
}
|
|
315
207
|
};
|
|
316
208
|
/**
|
|
317
|
-
* Opens a browser directory picker and returns a BrowserFilesApi instance.
|
|
318
|
-
* Only works in secure contexts (HTTPS) and requires user gesture.
|
|
319
|
-
*/
|
|
320
|
-
async function openBrowserFilesApi() {
|
|
321
|
-
return new BrowserFilesApi({ rootHandle: await globalThis.showDirectoryPicker() });
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
209
|
* Gets a BrowserFilesApi instance backed by the Origin Private File System (OPFS).
|
|
325
|
-
* OPFS provides a sandboxed filesystem that persists between sessions.
|
|
326
210
|
*/
|
|
327
211
|
async function getOPFSFilesApi() {
|
|
328
212
|
return new BrowserFilesApi({ rootHandle: await navigator.storage.getDirectory() });
|
|
329
213
|
}
|
|
330
|
-
|
|
331
214
|
//#endregion
|
|
332
|
-
|
|
215
|
+
//#region src/open-browser-files-api.ts
|
|
216
|
+
/**
|
|
217
|
+
* Utilities for opening and managing browser file system access.
|
|
218
|
+
*
|
|
219
|
+
* Provides functions for permission verification, handle accessibility checking,
|
|
220
|
+
* and opening directory pickers with optional persistent storage support.
|
|
221
|
+
*/
|
|
222
|
+
/**
|
|
223
|
+
* Checks if a file system handle is still accessible by performing a real read operation.
|
|
224
|
+
* Handles may become inaccessible if the underlying file/directory was moved or deleted.
|
|
225
|
+
*
|
|
226
|
+
* @param fileHandle - The file or directory handle to check
|
|
227
|
+
* @returns True if the handle is accessible, false otherwise
|
|
228
|
+
*/
|
|
229
|
+
async function isHandlerAccessible(fileHandle) {
|
|
230
|
+
try {
|
|
231
|
+
if (fileHandle.kind === "file") await fileHandle.getFile();
|
|
232
|
+
else for await (const _item of fileHandle.values()) break;
|
|
233
|
+
return true;
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Verifies and requests file system permissions for a handle.
|
|
240
|
+
* First checks if permission was already granted, then requests if needed.
|
|
241
|
+
*
|
|
242
|
+
* @param fileHandle - The file or directory handle to verify permissions for
|
|
243
|
+
* @param readWrite - Whether to request read-write access (true) or read-only (false)
|
|
244
|
+
* @returns True if permission is granted, false otherwise
|
|
245
|
+
*/
|
|
246
|
+
async function verifyPermission(fileHandle, readWrite = false) {
|
|
247
|
+
const handle = fileHandle;
|
|
248
|
+
const options = {};
|
|
249
|
+
if (readWrite) options.mode = "readwrite";
|
|
250
|
+
if (await handle.queryPermission(options) === "granted") return true;
|
|
251
|
+
if (await handle.requestPermission(options) === "granted") return true;
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Opens a browser directory picker and returns a BrowserFilesApi instance.
|
|
256
|
+
* Only works in secure contexts (HTTPS) and requires user gesture.
|
|
257
|
+
*
|
|
258
|
+
* Supports persistent storage of directory handles via optional get/set/del functions.
|
|
259
|
+
* When persistence is configured, will attempt to reuse a previously selected directory.
|
|
260
|
+
*
|
|
261
|
+
* @param options - Configuration options for opening the files API
|
|
262
|
+
* @returns A BrowserFilesApi instance for the selected directory
|
|
263
|
+
* @throws Error if access is not granted or the directory is not accessible
|
|
264
|
+
*/
|
|
265
|
+
async function openBrowserFilesApi(options = {}) {
|
|
266
|
+
const { handlerKey = "root-dir", readwrite = true, get = async () => void 0, set = async () => {}, del = async () => {} } = options;
|
|
267
|
+
let rootHandle = await get(handlerKey);
|
|
268
|
+
if (!rootHandle) {
|
|
269
|
+
rootHandle = await globalThis.showDirectoryPicker();
|
|
270
|
+
await set(handlerKey, rootHandle);
|
|
271
|
+
}
|
|
272
|
+
if (!await verifyPermission(rootHandle, readwrite)) throw new Error("Access was not granted");
|
|
273
|
+
if (!await isHandlerAccessible(rootHandle)) {
|
|
274
|
+
await del(handlerKey);
|
|
275
|
+
throw new Error("Cannot access the folder. Please try again.");
|
|
276
|
+
}
|
|
277
|
+
return new BrowserFilesApi({ rootHandle });
|
|
278
|
+
}
|
|
279
|
+
//#endregion
|
|
280
|
+
export { BrowserFilesApi, getOPFSFilesApi, isHandlerAccessible, openBrowserFilesApi, verifyPermission };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* webrun-files-browser - Browser File System Access API implementation
|
|
3
3
|
*/
|
|
4
|
-
export { BrowserFilesApi,
|
|
5
|
-
export {
|
|
4
|
+
export { BrowserFilesApi, type BrowserFilesApiOptions, getOPFSFilesApi, } from "./browser-files-api.js";
|
|
5
|
+
export { isHandlerAccessible, type OpenBrowserFilesApiOptions, openBrowserFilesApi, verifyPermission, } from "./open-browser-files-api.js";
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,eAAe,EACf,KAAK,sBAAsB,EAC3B,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for opening and managing browser file system access.
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for permission verification, handle accessibility checking,
|
|
5
|
+
* and opening directory pickers with optional persistent storage support.
|
|
6
|
+
*/
|
|
7
|
+
import { BrowserFilesApi } from "./browser-files-api.js";
|
|
8
|
+
/**
|
|
9
|
+
* Options for opening a browser files API with persistent storage support.
|
|
10
|
+
*/
|
|
11
|
+
export interface OpenBrowserFilesApiOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Key used to store the directory handler in persistent storage.
|
|
14
|
+
* @default "root-dir"
|
|
15
|
+
*/
|
|
16
|
+
handlerKey?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Whether to request read-write access (true) or read-only (false).
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
readwrite?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Gets a stored directory handler by key.
|
|
24
|
+
* @default Returns undefined (no persistence)
|
|
25
|
+
*/
|
|
26
|
+
get?: (key: string) => Promise<FileSystemDirectoryHandle | undefined>;
|
|
27
|
+
/**
|
|
28
|
+
* Stores a directory handler with a key.
|
|
29
|
+
* @default No-op (no persistence)
|
|
30
|
+
*/
|
|
31
|
+
set?: (key: string, handler: FileSystemDirectoryHandle) => Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Deletes a stored directory handler by key.
|
|
34
|
+
* @default No-op (no persistence)
|
|
35
|
+
*/
|
|
36
|
+
del?: (key: string) => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Checks if a file system handle is still accessible by performing a real read operation.
|
|
40
|
+
* Handles may become inaccessible if the underlying file/directory was moved or deleted.
|
|
41
|
+
*
|
|
42
|
+
* @param fileHandle - The file or directory handle to check
|
|
43
|
+
* @returns True if the handle is accessible, false otherwise
|
|
44
|
+
*/
|
|
45
|
+
export declare function isHandlerAccessible(fileHandle: FileSystemFileHandle | FileSystemDirectoryHandle): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Verifies and requests file system permissions for a handle.
|
|
48
|
+
* First checks if permission was already granted, then requests if needed.
|
|
49
|
+
*
|
|
50
|
+
* @param fileHandle - The file or directory handle to verify permissions for
|
|
51
|
+
* @param readWrite - Whether to request read-write access (true) or read-only (false)
|
|
52
|
+
* @returns True if permission is granted, false otherwise
|
|
53
|
+
*/
|
|
54
|
+
export declare function verifyPermission(fileHandle: FileSystemFileHandle | FileSystemDirectoryHandle, readWrite?: boolean): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Opens a browser directory picker and returns a BrowserFilesApi instance.
|
|
57
|
+
* Only works in secure contexts (HTTPS) and requires user gesture.
|
|
58
|
+
*
|
|
59
|
+
* Supports persistent storage of directory handles via optional get/set/del functions.
|
|
60
|
+
* When persistence is configured, will attempt to reuse a previously selected directory.
|
|
61
|
+
*
|
|
62
|
+
* @param options - Configuration options for opening the files API
|
|
63
|
+
* @returns A BrowserFilesApi instance for the selected directory
|
|
64
|
+
* @throws Error if access is not granted or the directory is not accessible
|
|
65
|
+
*/
|
|
66
|
+
export declare function openBrowserFilesApi(options?: OpenBrowserFilesApiOptions): Promise<BrowserFilesApi>;
|
|
67
|
+
//# sourceMappingURL=open-browser-files-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser-files-api.d.ts","sourceRoot":"","sources":["../src/open-browser-files-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAWzD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,yBAAyB,GAAG,SAAS,CAAC,CAAC;IACtE;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE;;;OAGG;IACH,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,oBAAoB,GAAG,yBAAyB,GAC3D,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,oBAAoB,GAAG,yBAAyB,EAC5D,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,eAAe,CAAC,CA6B1B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/webrun-files-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Browser File System Access API implementation for webrun-files",
|
|
@@ -28,25 +28,26 @@
|
|
|
28
28
|
"dist",
|
|
29
29
|
"src"
|
|
30
30
|
],
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "rimraf dist && rolldown -c && tsc --emitDeclarationOnly --declaration",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"lint": "biome lint src tests",
|
|
35
|
+
"prepublishOnly": "pnpm run build"
|
|
36
|
+
},
|
|
31
37
|
"dependencies": {
|
|
32
|
-
"@statewalker/webrun-files": "
|
|
38
|
+
"@statewalker/webrun-files": "workspace:*"
|
|
33
39
|
},
|
|
34
40
|
"devDependencies": {
|
|
35
|
-
"@
|
|
41
|
+
"@statewalker/webrun-files-tests": "workspace:*",
|
|
42
|
+
"@types/node": "catalog:",
|
|
36
43
|
"native-file-system-adapter": "^3.0.1",
|
|
37
|
-
"rimraf": "
|
|
38
|
-
"rolldown": "
|
|
39
|
-
"typescript": "
|
|
40
|
-
"vitest": "
|
|
41
|
-
"@statewalker/webrun-files-tests": "1.0.0"
|
|
44
|
+
"rimraf": "catalog:",
|
|
45
|
+
"rolldown": "catalog:",
|
|
46
|
+
"typescript": "catalog:",
|
|
47
|
+
"vitest": "catalog:"
|
|
42
48
|
},
|
|
43
49
|
"sideEffects": false,
|
|
44
50
|
"publishConfig": {
|
|
45
51
|
"access": "public"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"build": "rimraf dist && rolldown -c && tsc --emitDeclarationOnly --declaration",
|
|
49
|
-
"test": "vitest run",
|
|
50
|
-
"lint": "biome lint src tests"
|
|
51
52
|
}
|
|
52
|
-
}
|
|
53
|
+
}
|