@statewalker/webrun-files-browser 0.7.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 +6 -2
- package/dist/browser-files-api.d.ts.map +1 -1
- package/dist/cjs/index.cjs +36 -24
- package/dist/esm/index.js +15 -4
- package/package.json +15 -14
- package/src/browser-files-api.ts +31 -0
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ class BrowserFilesApi implements FilesApi {
|
|
|
107
107
|
stats(path: string): Promise<FileStats | undefined>;
|
|
108
108
|
exists(path: string): Promise<boolean>;
|
|
109
109
|
remove(path: string): Promise<boolean>;
|
|
110
|
-
move(source: string, target: string): Promise<boolean>;
|
|
110
|
+
move(source: string, target: string): Promise<boolean>; // uses native move when available
|
|
111
111
|
copy(source: string, target: string): Promise<boolean>;
|
|
112
112
|
}
|
|
113
113
|
```
|
|
@@ -141,11 +141,15 @@ function verifyPermission(
|
|
|
141
141
|
): Promise<boolean>;
|
|
142
142
|
```
|
|
143
143
|
|
|
144
|
+
## Native Move Support
|
|
145
|
+
|
|
146
|
+
The `move()` method attempts to use the browser's native [`FileSystemHandle.move()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle/move) when available (Chrome 110+, Edge 110+). This provides an atomic, in-place rename/move without copying file data. On older browsers or polyfills that lack native `move()`, the method falls back to copy-then-delete.
|
|
147
|
+
|
|
144
148
|
## Browser Support
|
|
145
149
|
|
|
146
150
|
The File System Access API is supported in:
|
|
147
151
|
|
|
148
|
-
- Chrome/Edge 86+
|
|
152
|
+
- Chrome/Edge 86+ (native `move()` from 110+)
|
|
149
153
|
- Opera 72+
|
|
150
154
|
- Chrome for Android 86+
|
|
151
155
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-files-api.d.ts","sourceRoot":"","sources":["../src/browser-files-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"browser-files-api.d.ts","sourceRoot":"","sources":["../src/browser-files-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,2BAA2B,CAAC;AAQnC,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,UAAU,EAAE,yBAAyB,CAAC;CACvC;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,QAAQ;IAC9C,OAAO,CAAC,UAAU,CAA4B;gBAElC,OAAO,EAAE,sBAAsB;YAI7B,kBAAkB;YAkBlB,aAAa;IAkBpB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC;IA6BrE,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC;IAsBV,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC;IAqCnE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAqCnD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKtC,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBtC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgCtD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CA4E7D;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,eAAe,CAAC,CAGhE"}
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _statewalker_webrun_files = require("@statewalker/webrun-files");
|
|
3
3
|
//#region src/browser-files-api.ts
|
|
4
4
|
/**
|
|
5
5
|
* Browser filesystem implementation using the File System Access API.
|
|
@@ -24,9 +24,9 @@ var BrowserFilesApi = class {
|
|
|
24
24
|
return current;
|
|
25
25
|
}
|
|
26
26
|
async getFileHandle(path, options = {}) {
|
|
27
|
-
const normalized = (0,
|
|
28
|
-
const parentPath = (0,
|
|
29
|
-
const fileName = (0,
|
|
27
|
+
const normalized = (0, _statewalker_webrun_files.normalizePath)(path);
|
|
28
|
+
const parentPath = (0, _statewalker_webrun_files.dirname)(normalized);
|
|
29
|
+
const fileName = (0, _statewalker_webrun_files.basename)(normalized);
|
|
30
30
|
const parent = await this.getDirectoryHandle(parentPath, options);
|
|
31
31
|
if (!parent) return null;
|
|
32
32
|
try {
|
|
@@ -59,9 +59,9 @@ var BrowserFilesApi = class {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
async write(path, content) {
|
|
62
|
-
const normalized = (0,
|
|
63
|
-
const parentPath = (0,
|
|
64
|
-
const fileName = (0,
|
|
62
|
+
const normalized = (0, _statewalker_webrun_files.normalizePath)(path);
|
|
63
|
+
const parentPath = (0, _statewalker_webrun_files.dirname)(normalized);
|
|
64
|
+
const fileName = (0, _statewalker_webrun_files.basename)(normalized);
|
|
65
65
|
const parent = await this.getDirectoryHandle(parentPath, { create: true });
|
|
66
66
|
if (!parent) throw new Error(`Cannot create directory: ${parentPath}`);
|
|
67
67
|
const writable = await (await parent.getFileHandle(fileName, { create: true })).createWritable();
|
|
@@ -72,16 +72,16 @@ var BrowserFilesApi = class {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
async mkdir(path) {
|
|
75
|
-
const normalized = (0,
|
|
75
|
+
const normalized = (0, _statewalker_webrun_files.normalizePath)(path);
|
|
76
76
|
await this.getDirectoryHandle(normalized, { create: true });
|
|
77
77
|
}
|
|
78
78
|
async *list(path, options) {
|
|
79
|
-
const normalized = (0,
|
|
79
|
+
const normalized = (0, _statewalker_webrun_files.normalizePath)(path);
|
|
80
80
|
const dirHandle = await this.getDirectoryHandle(normalized);
|
|
81
81
|
if (!dirHandle) return;
|
|
82
82
|
const recursive = options?.recursive ?? false;
|
|
83
83
|
for await (const [name, handle] of dirHandle.entries()) {
|
|
84
|
-
const entryPath = (0,
|
|
84
|
+
const entryPath = (0, _statewalker_webrun_files.joinPath)(normalized, name);
|
|
85
85
|
const isDirectory = handle.kind === "directory";
|
|
86
86
|
const info = {
|
|
87
87
|
kind: isDirectory ? "directory" : "file",
|
|
@@ -99,7 +99,7 @@ var BrowserFilesApi = class {
|
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
async stats(path) {
|
|
102
|
-
const normalized = (0,
|
|
102
|
+
const normalized = (0, _statewalker_webrun_files.normalizePath)(path);
|
|
103
103
|
if (normalized === "/") return {
|
|
104
104
|
kind: "directory",
|
|
105
105
|
lastModified: 0
|
|
@@ -124,9 +124,9 @@ var BrowserFilesApi = class {
|
|
|
124
124
|
return await this.stats(path) !== void 0;
|
|
125
125
|
}
|
|
126
126
|
async remove(path) {
|
|
127
|
-
const normalized = (0,
|
|
128
|
-
const parentPath = (0,
|
|
129
|
-
const name = (0,
|
|
127
|
+
const normalized = (0, _statewalker_webrun_files.normalizePath)(path);
|
|
128
|
+
const parentPath = (0, _statewalker_webrun_files.dirname)(normalized);
|
|
129
|
+
const name = (0, _statewalker_webrun_files.basename)(normalized);
|
|
130
130
|
const parent = await this.getDirectoryHandle(parentPath);
|
|
131
131
|
if (!parent) return false;
|
|
132
132
|
try {
|
|
@@ -137,20 +137,34 @@ var BrowserFilesApi = class {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
async move(source, target) {
|
|
140
|
+
const sourcePath = (0, _statewalker_webrun_files.normalizePath)(source);
|
|
141
|
+
const targetPath = (0, _statewalker_webrun_files.normalizePath)(target);
|
|
142
|
+
const targetParentPath = (0, _statewalker_webrun_files.dirname)(targetPath);
|
|
143
|
+
const targetName = (0, _statewalker_webrun_files.basename)(targetPath);
|
|
144
|
+
const sourceStats = await this.stats(sourcePath);
|
|
145
|
+
if (!sourceStats) return false;
|
|
146
|
+
const sourceHandle = sourceStats.kind === "file" ? await this.getFileHandle(sourcePath) : await this.getDirectoryHandle(sourcePath);
|
|
147
|
+
if (!sourceHandle) return false;
|
|
148
|
+
if (typeof sourceHandle.move === "function") try {
|
|
149
|
+
const targetParent = await this.getDirectoryHandle(targetParentPath, { create: true });
|
|
150
|
+
if (!targetParent) return false;
|
|
151
|
+
await sourceHandle.move(targetParent, targetName);
|
|
152
|
+
return true;
|
|
153
|
+
} catch {}
|
|
140
154
|
if (!await this.copy(source, target)) return false;
|
|
141
155
|
return this.remove(source);
|
|
142
156
|
}
|
|
143
157
|
async copy(source, target) {
|
|
144
|
-
const sourcePath = (0,
|
|
145
|
-
const targetPath = (0,
|
|
158
|
+
const sourcePath = (0, _statewalker_webrun_files.normalizePath)(source);
|
|
159
|
+
const targetPath = (0, _statewalker_webrun_files.normalizePath)(target);
|
|
146
160
|
const sourceStats = await this.stats(source);
|
|
147
161
|
if (!sourceStats) return false;
|
|
148
162
|
if (sourceStats.kind === "file") {
|
|
149
163
|
const sourceHandle = await this.getFileHandle(sourcePath);
|
|
150
164
|
if (!sourceHandle) return false;
|
|
151
165
|
const sourceFile = await sourceHandle.getFile();
|
|
152
|
-
const targetParentPath = (0,
|
|
153
|
-
const targetFileName = (0,
|
|
166
|
+
const targetParentPath = (0, _statewalker_webrun_files.dirname)(targetPath);
|
|
167
|
+
const targetFileName = (0, _statewalker_webrun_files.basename)(targetPath);
|
|
154
168
|
const targetParent = await this.getDirectoryHandle(targetParentPath, { create: true });
|
|
155
169
|
if (!targetParent) return false;
|
|
156
170
|
const writable = await (await targetParent.getFileHandle(targetFileName, { create: true })).createWritable();
|
|
@@ -173,8 +187,8 @@ var BrowserFilesApi = class {
|
|
|
173
187
|
const entryHandle = await this.getFileHandle(entry.path);
|
|
174
188
|
if (!entryHandle) continue;
|
|
175
189
|
const entryFile = await entryHandle.getFile();
|
|
176
|
-
const newTargetParentPath = (0,
|
|
177
|
-
const newTargetFileName = (0,
|
|
190
|
+
const newTargetParentPath = (0, _statewalker_webrun_files.dirname)(newTargetPath);
|
|
191
|
+
const newTargetFileName = (0, _statewalker_webrun_files.basename)(newTargetPath);
|
|
178
192
|
const newTargetParent = await this.getDirectoryHandle(newTargetParentPath, { create: true });
|
|
179
193
|
if (!newTargetParent) continue;
|
|
180
194
|
const writable = await (await newTargetParent.getFileHandle(newTargetFileName, { create: true })).createWritable();
|
|
@@ -198,7 +212,6 @@ var BrowserFilesApi = class {
|
|
|
198
212
|
async function getOPFSFilesApi() {
|
|
199
213
|
return new BrowserFilesApi({ rootHandle: await navigator.storage.getDirectory() });
|
|
200
214
|
}
|
|
201
|
-
|
|
202
215
|
//#endregion
|
|
203
216
|
//#region src/open-browser-files-api.ts
|
|
204
217
|
/**
|
|
@@ -264,10 +277,9 @@ async function openBrowserFilesApi(options = {}) {
|
|
|
264
277
|
}
|
|
265
278
|
return new BrowserFilesApi({ rootHandle });
|
|
266
279
|
}
|
|
267
|
-
|
|
268
280
|
//#endregion
|
|
269
281
|
exports.BrowserFilesApi = BrowserFilesApi;
|
|
270
282
|
exports.getOPFSFilesApi = getOPFSFilesApi;
|
|
271
283
|
exports.isHandlerAccessible = isHandlerAccessible;
|
|
272
284
|
exports.openBrowserFilesApi = openBrowserFilesApi;
|
|
273
|
-
exports.verifyPermission = verifyPermission;
|
|
285
|
+
exports.verifyPermission = verifyPermission;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { basename, dirname, joinPath, normalizePath } from "@statewalker/webrun-files";
|
|
2
|
-
|
|
3
2
|
//#region src/browser-files-api.ts
|
|
4
3
|
/**
|
|
5
4
|
* Browser filesystem implementation using the File System Access API.
|
|
@@ -137,6 +136,20 @@ var BrowserFilesApi = class {
|
|
|
137
136
|
}
|
|
138
137
|
}
|
|
139
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 {}
|
|
140
153
|
if (!await this.copy(source, target)) return false;
|
|
141
154
|
return this.remove(source);
|
|
142
155
|
}
|
|
@@ -198,7 +211,6 @@ var BrowserFilesApi = class {
|
|
|
198
211
|
async function getOPFSFilesApi() {
|
|
199
212
|
return new BrowserFilesApi({ rootHandle: await navigator.storage.getDirectory() });
|
|
200
213
|
}
|
|
201
|
-
|
|
202
214
|
//#endregion
|
|
203
215
|
//#region src/open-browser-files-api.ts
|
|
204
216
|
/**
|
|
@@ -264,6 +276,5 @@ async function openBrowserFilesApi(options = {}) {
|
|
|
264
276
|
}
|
|
265
277
|
return new BrowserFilesApi({ rootHandle });
|
|
266
278
|
}
|
|
267
|
-
|
|
268
279
|
//#endregion
|
|
269
|
-
export { BrowserFilesApi, getOPFSFilesApi, isHandlerAccessible, openBrowserFilesApi, verifyPermission };
|
|
280
|
+
export { BrowserFilesApi, getOPFSFilesApi, isHandlerAccessible, openBrowserFilesApi, verifyPermission };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/webrun-files-browser",
|
|
3
|
-
"version": "0.7.
|
|
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
|
+
}
|
package/src/browser-files-api.ts
CHANGED
|
@@ -14,6 +14,11 @@ import type {
|
|
|
14
14
|
} from "@statewalker/webrun-files";
|
|
15
15
|
import { basename, dirname, joinPath, normalizePath } from "@statewalker/webrun-files";
|
|
16
16
|
|
|
17
|
+
/** Chrome 110+ exposes move() on FileSystemHandle; not yet in TS DOM types. */
|
|
18
|
+
interface MovableHandle extends FileSystemHandle {
|
|
19
|
+
move(destination: FileSystemDirectoryHandle, name: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
17
22
|
export interface BrowserFilesApiOptions {
|
|
18
23
|
/**
|
|
19
24
|
* Root FileSystemDirectoryHandle to use as the filesystem root.
|
|
@@ -227,6 +232,32 @@ export class BrowserFilesApi implements FilesApi {
|
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
async move(source: string, target: string): Promise<boolean> {
|
|
235
|
+
const sourcePath = normalizePath(source);
|
|
236
|
+
const targetPath = normalizePath(target);
|
|
237
|
+
const targetParentPath = dirname(targetPath);
|
|
238
|
+
const targetName = basename(targetPath);
|
|
239
|
+
|
|
240
|
+
// Try native move() (available in Chrome 110+, Edge 110+)
|
|
241
|
+
const sourceStats = await this.stats(sourcePath);
|
|
242
|
+
if (!sourceStats) return false;
|
|
243
|
+
|
|
244
|
+
const sourceHandle: FileSystemHandle | null =
|
|
245
|
+
sourceStats.kind === "file"
|
|
246
|
+
? await this.getFileHandle(sourcePath)
|
|
247
|
+
: await this.getDirectoryHandle(sourcePath);
|
|
248
|
+
if (!sourceHandle) return false;
|
|
249
|
+
|
|
250
|
+
if (typeof (sourceHandle as MovableHandle).move === "function") {
|
|
251
|
+
try {
|
|
252
|
+
const targetParent = await this.getDirectoryHandle(targetParentPath, { create: true });
|
|
253
|
+
if (!targetParent) return false;
|
|
254
|
+
await (sourceHandle as MovableHandle).move(targetParent, targetName);
|
|
255
|
+
return true;
|
|
256
|
+
} catch {
|
|
257
|
+
// Fall through to copy-then-delete
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
230
261
|
const copied = await this.copy(source, target);
|
|
231
262
|
if (!copied) return false;
|
|
232
263
|
return this.remove(source);
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022-2026 statewalker
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|