@statewalker/webrun-files 0.3.0 → 0.4.0

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.
Files changed (51) hide show
  1. package/dist/cjs/index.cjs +564 -0
  2. package/dist/esm/index.js +551 -0
  3. package/dist/files-api.d.ts +44 -0
  4. package/dist/files-api.d.ts.map +1 -0
  5. package/dist/impl/index.d.ts +6 -0
  6. package/dist/impl/index.d.ts.map +1 -0
  7. package/dist/impl/mem-files-api.d.ts +13 -0
  8. package/dist/impl/mem-files-api.d.ts.map +1 -0
  9. package/dist/impl/node-files-api.d.ts +24 -0
  10. package/dist/impl/node-files-api.d.ts.map +1 -0
  11. package/dist/index.d.ts +11 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/types.d.ts +57 -0
  14. package/dist/types.d.ts.map +1 -0
  15. package/dist/utils/collect-stream.d.ts +16 -0
  16. package/dist/utils/collect-stream.d.ts.map +1 -0
  17. package/dist/utils/index.d.ts +7 -0
  18. package/dist/utils/index.d.ts.map +1 -0
  19. package/dist/utils/normalize-path.d.ts +21 -0
  20. package/dist/utils/normalize-path.d.ts.map +1 -0
  21. package/dist/utils/path-utils.d.ts +20 -0
  22. package/dist/utils/path-utils.d.ts.map +1 -0
  23. package/package.json +29 -31
  24. package/src/files-api.ts +167 -0
  25. package/src/impl/index.ts +6 -0
  26. package/src/impl/mem-files-api.ts +306 -0
  27. package/src/impl/node-files-api.ts +261 -0
  28. package/src/index.ts +35 -0
  29. package/src/types.ts +68 -0
  30. package/src/utils/collect-stream.ts +35 -0
  31. package/src/utils/index.ts +7 -0
  32. package/src/utils/normalize-path.ts +34 -0
  33. package/src/utils/path-utils.ts +46 -0
  34. package/CHANGELOG.md +0 -2
  35. package/README.md +0 -17
  36. package/dist/index-umd.js +0 -1053
  37. package/dist/index-umd.min.js +0 -2
  38. package/dist/index.js +0 -1032
  39. package/dist/index.min.js +0 -2
  40. package/index.js +0 -1
  41. package/src/BrowserFilesApi.js +0 -335
  42. package/src/FilesApi.js +0 -133
  43. package/src/MemFilesApi.js +0 -129
  44. package/src/NodeFilesApi.js +0 -222
  45. package/src/addFilesApiLogger.js +0 -25
  46. package/src/getMimeType.js +0 -19
  47. package/src/index.js +0 -10
  48. package/src/mimeTypes.js +0 -96
  49. package/src/normalizePath.js +0 -8
  50. package/src/notImplemented.js +0 -3
  51. package/src/openBrowserFilesApi.js +0 -67
@@ -1,222 +0,0 @@
1
- import FilesApi from "./FilesApi.js";
2
- import getMimeType from "./getMimeType.js";
3
-
4
- export default class NodeFilesApi extends FilesApi {
5
- constructor(options) {
6
- super(options);
7
- if (!this.fs) throw new Error("File System is not defined.");
8
- if (!this.rootDir) throw new Error("Root directory is not defined.");
9
- }
10
-
11
- get rootDirSegments() {
12
- if (!this._rootDirSegments) {
13
- this._rootDirSegments = this._toPathSegments(
14
- this.options.rootDir || "./",
15
- );
16
- }
17
- return this._rootDirSegments;
18
- }
19
-
20
- get rootDir() {
21
- if (!this._rootDir) {
22
- this._rootDir = this.rootDirSegments.join("/");
23
- }
24
- return this._rootDir;
25
- }
26
-
27
- get fs() {
28
- return this.options.fs;
29
- }
30
-
31
- async _init() {
32
- return this._initialized = this._initialized ||
33
- (async () => {
34
- const dirPath = this.rootDir;
35
- return await mkdir(this.fs, dirPath);
36
- })();
37
- }
38
-
39
- async *list(file, { recursive = false } = {}) {
40
- await this._init();
41
- const filePath = this._expandPath(file);
42
- const fileInfo = await this._stats(filePath);
43
- if (!fileInfo || fileInfo.kind !== "directory") return;
44
- for await (let f of list(this.fs, filePath, recursive)) {
45
- yield this._getFileInfo(f.path, f.stat);
46
- }
47
- }
48
-
49
- async stats(file) {
50
- await this._init();
51
- const filePath = this._expandPath(file);
52
- return this._stats(filePath);
53
- }
54
-
55
- async remove(file) {
56
- await this._init();
57
- const filePath = this._expandPath(file);
58
- return await remove(this.fs, filePath);
59
- }
60
-
61
- async write(file, content) {
62
- await this._init();
63
- const filePath = this._expandPath(file);
64
- const dir = pathDirname(filePath);
65
- await this.fs.mkdir(dir, { recursive: true });
66
- let handle;
67
- try {
68
- handle = await this.fs.open(filePath, "w");
69
- if (typeof content === "function") content = content();
70
- await handle.writeFile(content);
71
- } finally {
72
- handle && await handle.close();
73
- }
74
- }
75
-
76
- async *read(file, { start = 0, bufferSize = 1024 * 8 } = {}) {
77
- await this._init();
78
- const filePath = this._expandPath(file);
79
- let handle;
80
- try {
81
- handle = await this.fs.open(filePath, "r");
82
- const offset = 0;
83
- for (let position = start, bytesRead = 0; true; position += bytesRead) {
84
- const buffer = new Uint8ClampedArray(bufferSize);
85
- const result = await handle.read(
86
- buffer,
87
- offset,
88
- bufferSize,
89
- position,
90
- );
91
- bytesRead = result.bytesRead;
92
- if (bytesRead === 0) break;
93
- yield bytesRead < bufferSize
94
- ? result.buffer.slice(0, bytesRead)
95
- : result.buffer;
96
- }
97
- } finally {
98
- handle && await handle.close();
99
- }
100
- }
101
-
102
- // TODO:
103
- // async copy(fromPath, toPath, options = {}) { ... }
104
-
105
- // TODO:
106
- // async move(fromPath, toPath, options = {}) { ... }
107
-
108
- async _stats(filePath) {
109
- try {
110
- const stat = await this.fs.stat(filePath);
111
- return this._getFileInfo(filePath, stat);
112
- } catch (err) {
113
- if (err.code !== "ENOENT") throw err;
114
- return null;
115
- }
116
- }
117
-
118
- _getFileInfo(path, stat) {
119
- const segments = this._toPathSegments(path).slice(
120
- this.rootDirSegments.length,
121
- );
122
- segments.unshift("");
123
- const name = segments[segments.length - 1];
124
- path = segments.join("/");
125
- // const name = path.split("/").filter((s) => !!s).pop() || "";
126
- let info = {
127
- path,
128
- name,
129
- kind: "",
130
- };
131
- if (!stat.isFile()) {
132
- info.kind = "directory";
133
- } else {
134
- info.size = stat.size || 0;
135
- info.type = getMimeType(path);
136
- info.kind = "file";
137
- info.lastModified = new Date(stat.mtime).getTime();
138
- }
139
- return info;
140
- }
141
-
142
- _toPathSegments(filePath) {
143
- filePath = this.normalizePath(filePath);
144
- const segments = filePath.split("/"); // .filter((s) => !!s);
145
- // if (segments.length === 0) segments.push("");
146
- // segments.unshift("");
147
- return segments;
148
- }
149
-
150
- _expandPath(file) {
151
- const filePath = typeof file === "object" ? file.path : file + "";
152
- const segments = this._toPathSegments(filePath).filter((s) => !!s);
153
- // segments.shift(); // The first segment is always empty
154
- segments.unshift(...this.rootDirSegments);
155
- return segments.join("/");
156
- }
157
- }
158
-
159
- export function pathDirname(path) {
160
- const segments = path.split("/");
161
- segments.pop();
162
- return segments.join("/");
163
- }
164
-
165
- export function pathJoin(...paths) {
166
- let segments = paths
167
- .map((path) => path.split("/"))
168
- .reduce((result, segments) => (result.push(...segments), result), []);
169
- const firstSegment = segments.shift();
170
- segments = segments.filter((s) => !!s && s !== ".");
171
- segments.unshift(firstSegment);
172
- return segments.join("/");
173
- }
174
-
175
- export async function mkdir(fs, dir) {
176
- try {
177
- const stat = await fs.stat(dir);
178
- if (stat && stat.isDirectory()) return true;
179
- } catch (error) {
180
- // Just ignore
181
- }
182
- const parent = pathDirname(dir);
183
- if (dir === parent) return false;
184
- await mkdir(fs, parent);
185
- await fs.mkdir(dir, { recursive: true });
186
- return true;
187
- }
188
-
189
- export async function remove(fs, path) {
190
- try {
191
- let stat = await fs.stat(path);
192
- if (stat.isDirectory()) {
193
- for await (let file of list(fs, path, false)) {
194
- await remove(fs, file.path);
195
- }
196
- await fs.rmdir(path);
197
- } else {
198
- await fs.unlink(path);
199
- }
200
- } catch (err) {
201
- if (err.code !== "ENOENT") throw err;
202
- }
203
- }
204
-
205
- export async function* list(fs, filePath, recursive) {
206
- try {
207
- const stat = await fs.stat(filePath);
208
- if (stat.isDirectory()) {
209
- const array = await fs.readdir(filePath);
210
- for (const name of array.sort()) {
211
- const f = pathJoin(filePath, name);
212
- let stat = await fs.stat(f);
213
- yield { path: f, stat };
214
- if (recursive && stat.isDirectory()) {
215
- yield* list(fs, f, true);
216
- }
217
- }
218
- }
219
- } catch (err) {
220
- if (err.code !== "ENOENT") throw err;
221
- }
222
- }
@@ -1,25 +0,0 @@
1
- export default function addFilesApiLogger({
2
- filesApi,
3
- log = console.log,
4
- wrapper = {},
5
- }) {
6
- return ["list", "stats", "remove", "write", "read", "copy", "move"].reduce(
7
- (wrapper, methodName) => {
8
- const method = filesApi[methodName].bind(filesApi);
9
- wrapper[methodName] = (...args) => {
10
- let message = { method: methodName, stage : "enter", args };
11
- try {
12
- log(message);
13
- return message.result = method.call(filesApi, ...args);
14
- } catch (e) {
15
- message.error = e;
16
- } finally {
17
- message.stage = "exit";
18
- log(message);
19
- }
20
- };
21
- return wrapper;
22
- },
23
- wrapper,
24
- );
25
- }
@@ -1,19 +0,0 @@
1
- // import mimeDb from 'mime-db';
2
- // import mimeTypes from 'mime-types';
3
- import { extensionToMimeType } from "./mimeTypes.js";
4
-
5
- export default function getMimeType(url) {
6
- url = url || '';
7
- const originalUrl = url;
8
- url = url.split('?').shift(); // Remove queries (if any)
9
- url = url.split('/').pop(); // Keep only the last segment of the path
10
- const extension = url.split('.').pop(); // Get extension
11
- let mimeType = extensionToMimeType[extension];
12
- if (!mimeType) {
13
- mimeType =
14
- originalUrl.match(/api\.observablehq\.com/) || originalUrl.match(/\.ojs$/)
15
- ? 'application/javascript-observable'
16
- : 'application/binary';
17
- }
18
- return mimeType;
19
- }
package/src/index.js DELETED
@@ -1,10 +0,0 @@
1
- export { default as FilesApi } from "./FilesApi.js";
2
- export { default as MemFilesApi } from "./MemFilesApi.js";
3
- export { default as NodeFilesApi } from "./NodeFilesApi.js";
4
- export { default as BrowserFilesApi } from "./BrowserFilesApi.js";
5
- export { default as normalizePath } from "./normalizePath.js";
6
- export { default as addFilesApiLogger } from "./addFilesApiLogger.js";
7
-
8
- export { default as getMimeType } from "./getMimeType.js";
9
- export * from "./mimeTypes.js";
10
- export * from "./openBrowserFilesApi.js";
package/src/mimeTypes.js DELETED
@@ -1,96 +0,0 @@
1
- // https://docs.w3cub.com/http/basics_of_http/mime_types/complete_list_of_mime_types
2
- export const mimeTypes = [
3
- { "extensions": ["aac"], "mime": "audio/aac", "description": "AAC audio" },
4
- { "extensions": ["abw"], "mime": "application/x-abiword", "description": "AbiWord document" },
5
- { "extensions": ["arc"], "mime": "application/x-freearc", "description": "Archive document (multiple files embedded)" },
6
- { "extensions": ["avi"], "mime": "video/x-msvideo", "description": "AVI: Audio Video Interleave" },
7
- { "extensions": ["azw"], "mime": "application/vnd.amazon.ebook", "description": "Amazon Kindle eBook format" },
8
- { "extensions": ["bin"], "mime": "application/octet-stream", "description": "Any kind of binary data" },
9
- { "extensions": ["bmp"], "mime": "image/bmp", "description": "Windows OS/2 Bitmap Graphics" },
10
- { "extensions": ["bz"], "mime": "application/x-bzip", "description": "BZip archive" },
11
- { "extensions": ["bz2"], "mime": "application/x-bzip2", "description": "BZip2 archive" },
12
- { "extensions": ["csh"], "mime": "application/x-csh", "description": "C-Shell script" },
13
- { "extensions": ["css"], "mime": "text/css", "description": "Cascading Style Sheets (CSS)" },
14
- { "extensions": ["csv"], "mime": "text/csv", "description": "Comma-separated values (CSV)" },
15
- { "extensions": ["doc"], "mime": "application/msword", "description": "Microsoft Word" },
16
- { "extensions": ["docx"], "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "description": "Microsoft Word (OpenXML)" },
17
- { "extensions": ["eot"], "mime": "application/vnd.ms-fontobject", "description": "MS Embedded OpenType fonts" },
18
- { "extensions": ["epub"], "mime": "application/epub+zip", "description": "Electronic publication (EPUB)" },
19
- { "extensions": ["gz"], "mime": "application/gzip", "description": "GZip Compressed Archive" },
20
- { "extensions": ["gif"], "mime": "image/gif", "description": "Graphics Interchange Format (GIF)" },
21
- { "extensions": ["htm", "html"], "mime": "text/html", "description": "HyperText Markup Language (HTML)" },
22
- { "extensions": ["ico"], "mime": "image/vnd.microsoft.icon", "description": "Icon format" },
23
- { "extensions": ["ics"], "mime": "text/calendar", "description": "iCalendar format" },
24
- { "extensions": ["jar"], "mime": "application/java-archive", "description": "Java Archive (JAR)" },
25
- { "extensions": ["jpg", "jpeg"], "mime": "image/jpeg", "description": "JPEG images" },
26
- { "extensions": ["js"], "mime": "text/javascript", "description": "JavaScript per the following specifications: https://html.spec.whatwg.org/multipage/#scriptingLanguages, https://html.spec.whatwg.org/multipage/#dependencies:willful-violation, https://datatracker.ietf.org/doc/draft-ietf-dispatch-javascript-mjs/" },
27
- { "extensions": ["json"], "mime": "application/json", "description": "JSON format" },
28
- { "extensions": ["jsonld"], "mime": "application/ld+json", "description": "JSON-LD format" },
29
- { "extensions": ["mid", "midi"], "mime": "application/ld+json", "description": "Musical Instrument Digital Interface (MIDI) audio/midi audio/x-midi" },
30
- { "extensions": ["mjs"], "mime": "text/javascript", "description": "JavaScript module" },
31
- { "extensions": ["mp3"], "mime": "audio/mpeg", "description": "MP3 audio" },
32
- { "extensions": ["md"], "mime": "text/markdown", "description": "Markdown" },
33
- { "extensions": ["mpeg"], "mime": "video/mpeg", "description": "MPEG Video" },
34
- { "extensions": ["mpkg"], "mime": "application/vnd.apple.installer+xml", "description": "Apple Installer Package" },
35
- { "extensions": ["odp"], "mime": "application/vnd.oasis.opendocument.presentation", "description": "OpenDocument presentation document" },
36
- { "extensions": ["ods"], "mime": "application/vnd.oasis.opendocument.spreadsheet", "description": "OpenDocument spreadsheet document" },
37
- { "extensions": ["odt"], "mime": "application/vnd.oasis.opendocument.text", "description": "OpenDocument text document" },
38
- { "extensions": ["oga"], "mime": "audio/ogg", "description": "OGG audio" },
39
- { "extensions": ["ogv"], "mime": "video/ogg", "description": "OGG video" },
40
- { "extensions": ["ogx"], "mime": "application/ogg", "description": "OGG" },
41
- { "extensions": ["opus"], "mime": "audio/opus", "description": "Opus audio" },
42
- { "extensions": ["otf"], "mime": "font/otf", "description": "OpenType font" },
43
- { "extensions": ["png"], "mime": "image/png", "description": "Portable Network Graphics" },
44
- { "extensions": ["pdf"], "mime": "application/pdf", "description": "Adobe Portable Document Format (PDF)" },
45
- { "extensions": ["php"], "mime": "application/x-httpd-php", "description": "Hypertext Preprocessor (Personal Home Page)" },
46
- { "extensions": ["ppt"], "mime": "application/vnd.ms-powerpoint", "description": "Microsoft PowerPoint" },
47
- { "extensions": ["pptx"], "mime": "application/vnd.openxmlformats-officedocument.presentationml.presentation", "description": "Microsoft PowerPoint (OpenXML)" },
48
- { "extensions": ["rar"], "mime": "application/vnd.rar", "description": "RAR archive" },
49
- { "extensions": ["rtf"], "mime": "application/rtf", "description": "Rich Text Format (RTF)" },
50
- { "extensions": ["sh"], "mime": "application/x-sh", "description": "Bourne shell script" },
51
- { "extensions": ["svg"], "mime": "image/svg+xml", "description": "Scalable Vector Graphics (SVG)" },
52
- { "extensions": ["swf"], "mime": "application/x-shockwave-flash", "description": "Small web format (SWF) or Adobe Flash document" },
53
- { "extensions": ["tar"], "mime": "application/x-tar", "description": "Tape Archive (TAR)" },
54
- { "extensions": ["tif", "tiff"], "mime": "image/tiff", "description": "Tagged Image File Format (TIFF)" },
55
- // { "extensions": ["ts"], "mime": "video/mp2t", "description": "MPEG transport stream" },
56
- { "extensions": ["ttf"], "mime": "font/ttf", "description": "TrueType Font" },
57
- { "extensions": ["txt"], "mime": "text/plain", "description": "Text, (generally ASCII or ISO 8859-n)" },
58
- { "extensions": ["vsd"], "mime": "application/vnd.visio", "description": "Microsoft Visio" },
59
- { "extensions": ["wav"], "mime": "audio/wav", "description": "Waveform Audio Format" },
60
- { "extensions": ["weba"], "mime": "audio/webm", "description": "WEBM audio" },
61
- { "extensions": ["webm"], "mime": "video/webm", "description": "WEBM video" },
62
- { "extensions": ["webp"], "mime": "image/webp", "description": "WEBP image" },
63
- { "extensions": ["woff"], "mime": "font/woff", "description": "Web Open Font Format (WOFF)" },
64
- { "extensions": ["woff2"], "mime": "font/woff2", "description": "Web Open Font Format (WOFF)" },
65
- { "extensions": ["xhtml"], "mime": "application/xhtml+xml", "description": "XHTML" },
66
- { "extensions": ["xls"], "mime": "application/vnd.ms-excel", "description": "Microsoft Excel" },
67
- { "extensions": ["xlsx"], "mime": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "description": "Microsoft Excel (OpenXML)" },
68
- { "extensions": ["xml"], "mime": "application/xml", "description": "XML; application/xml - if not readable from casual users (RFC 3023, section 3); text/xml - if readable from casual users (RFC 3023, section 3)" },
69
- { "extensions": ["xul"], "mime": "application/vnd.mozilla.xul+xml", "description": "XUL" },
70
- { "extensions": ["zip"], "mime": "application/zip", "description": "ZIP archive" },
71
- { "extensions": ["3gp"], "mime": "video/3gpp", "description": "3GPP audio/video container" },
72
- { "extensions": ["3g2"], "mime": "video/3gpp2", "description": "3GPP2 audio/video container; audio/3gpp if it doesn't contain video; audio/3gpp2 if it doesn't contain video" },
73
- { "extensions": ["7z"], "mime": "application/x-7z-compressed", "description": "7-zip archive" },
74
-
75
- { "extensions": ["jsx"], "mime": "text/jsx", "description": "JavaScript Syntax Extension / JavaScript XML format" },
76
- { "extensions": ["ts"], "mime": "text/ts", "description": "TypeScript format" },
77
- { "extensions": ["tsx"], "mime": "text/tsx", "description": "TypeScript Syntax Extension / TypeScript XML format" },
78
-
79
- ]
80
-
81
- export const mimeTypesDescriptions = mimeTypes.reduce((index, t) => {
82
- index[t.mime] = t.description;
83
- return index;
84
- }, {});
85
-
86
- export const extensionToMimeType = mimeTypes.reduce((index, t) => {
87
- for (const ext of t.extensions) {
88
- index[ext] = t.mime;
89
- }
90
- return index;
91
- }, {});
92
-
93
- export const mimeTypeToExtensions = mimeTypes.reduce((index, t) => {
94
- index[t.mime] = t.extensions;
95
- return index;
96
- }, {});
@@ -1,8 +0,0 @@
1
- export default function normalizePath(filePath) {
2
- const segments = filePath.split("/").filter((s) => !!s && s !== ".");
3
- if (segments.length === 0) {
4
- segments.push("");
5
- }
6
- segments.unshift("");
7
- return segments.join("/");
8
- }
@@ -1,3 +0,0 @@
1
- export default function notImplemented(){
2
- throw new Error('Not implemented.');
3
- }
@@ -1,67 +0,0 @@
1
- import BrowserFilesApi from "./BrowserFilesApi.js";
2
-
3
- export async function openBrowserFilesApi({
4
- handlerKey = "root-dir",
5
- readwrite = true,
6
- // Methods below could be replaced by persistent implementation
7
- // using the "idb-keyval" package (methods "get", "set", "del").
8
- index = {},
9
- get = async (key) => index[key],
10
- set = async (key, handler) => index[key] = handler,
11
- del = async (key) => delete index[key],
12
- } = {}) {
13
- let rootHandler = await get(handlerKey);
14
- if (!rootHandler) {
15
- rootHandler = await window.showDirectoryPicker();
16
- await set(handlerKey, rootHandler);
17
- }
18
- if (!(await verifyPermission(rootHandler, readwrite))) {
19
- throw new Error("Access was not granted");
20
- }
21
- if (!(await isHandlerAccessible(rootHandler))) {
22
- // await set(handlerKey, null);
23
- await del(handlerKey);
24
- throw new Error("Can not access to the folder. Please try again.");
25
- }
26
- return new BrowserFilesApi({ rootHandler });
27
- }
28
-
29
- export async function isHandlerAccessible(fileHandle) {
30
- // We need to perform a real read operation with the handler
31
- // to check if the file/directory still exists.
32
- let exists = false;
33
- try {
34
- if (fileHandle.kind === "file") {
35
- await fileHandle.getFile();
36
- } else {
37
- for await (let item of fileHandle.values()) {
38
- break;
39
- }
40
- }
41
- exists = true;
42
- } catch (error) {
43
- console.log(error, fileHandle);
44
- exists = false;
45
- }
46
- return exists;
47
- }
48
-
49
- export async function verifyPermission(fileHandle, readWrite) {
50
- const options = {};
51
- if (readWrite) {
52
- options.mode = "readwrite";
53
- }
54
- let granted = false;
55
- // Check if permission was already granted. If so, return true.
56
- if ((await fileHandle.queryPermission(options)) === "granted") {
57
- granted = true;
58
- }
59
- // Request permission. If the user grants permission, return true.
60
- if (
61
- !granted && (await fileHandle.requestPermission(options)) === "granted"
62
- ) {
63
- granted = true;
64
- }
65
- // The user didn't grant permission, so return false.
66
- return granted;
67
- }